当前位置: 首页 > 知识库问答 >
问题:

线程:c中无限循环线程的终止

华心思
2023-03-14

我正在尝试编写一个线程,该线程将在我的主程序的后台运行,并监视某些内容。在某个时候,主程序应该向线程发出安全退出的信号。下面是一个以固定间隔将本地时间写入命令行的最小示例。

#include <cmath>
#include <iostream>
#include <thread>
#include <future>
#include <chrono>

int func(bool & on)
{
    while(on) {
        auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
        std::cout << ctime(&t) << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

int main()
{
  bool on = true;
  std::future<int> fi = std::async(std::launch::async, func, on);
  std::this_thread::sleep_for(std::chrono::seconds(5));
  on = false;
  return 0;
}

当“on”变量没有通过引用传递时,此代码会编译并产生预期的结果,只是线程永远不会终止。一旦变量通过引用传递,我就会收到编译器错误

In file included from /opt/extlib/gcc/5.2.0/gcc/5.2.0/include/c++/5.2.0/thread:39:0,
             from background_thread.cpp:3:
/opt/extlib/gcc/5.2.0/gcc/5.2.0/include/c++/5.2.0/functional: In instantiation of ‘struct std::_Bind_simple<int (*(bool))(bool&)>’:
/opt/extlib/gcc/5.2.0/gcc/5.2.0/include/c++/5.2.0/future:1709:67:   required from ‘std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> std::async(std::launch, _Fn&&, _Args&& ...) [with _Fn = int (&)(bool&); _Args = {bool&}; typename std::result_of<_Functor(_ArgTypes ...)>::type = int]’
background_thread.cpp:20:64:   required from here
/opt/extlib/gcc/5.2.0/gcc/5.2.0/include/c++/5.2.0/functional:1505:61: error: no type named ‘type’ in ‘class std::result_of<int (*(bool))(bool&)>’
   typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                         ^
/opt/extlib/gcc/5.2.0/gcc/5.2.0/include/c++/5.2.0/functional:1526:9: error: no type named ‘type’ in ‘class std::result_of<int (*(bool))(bool&)>’
     _M_invoke(_Index_tuple<_Indices...>)

您能建议一种修复此代码的方法吗?

额外的问题:哪里出了问题,为什么它可以与std::ref一起工作,而不能与normal一起工作

共有3个答案

陶寒
2023-03-14

额外的问题:哪里出了问题,为什么它可以与std::ref一起工作,而不能与normal一起工作

因为std::async以及类似的接口,如std::线程std::bind按值获取参数。当您通过值传递引用时,引用的对象将被复制为参数。当您使用std::reference_wrapper时,包装器被复制,其中的引用保持不变。

如果你想知道为什么它设计成这样,请查看下面的答案

伍弘盛
2023-03-14

我可以看出您的方法存在两个问题:

  • 正如《黑暗》所提到的,您正在引用一个可能超出范围的布尔值,因为您的main在将设置为=false
  • 之后存在
  • 您应该尝试让编译器意识到on的值可能会在循环执行期间发生变化。如果您不这样做,一些编译器优化可能会简单地用无限循环替换time。

一个简单的解决方案是将标志放在全局内存中:

static std::atomic<bool> on = false;

void func()
{
    while (on)
        /* ... */
} 

int main() 
{
    on = true;
    /* launch thread */
    /* wait */
    on = false;
    return 0;
}
计燕七
2023-03-14

std::ref是一个开始,但还不够。只有当另一个线程保护一个变量时,c才能保证知道该变量的更改,

a) 原子,或

记忆栅栏(互斥锁、condition_variable等)

在允许main完成之前同步线程也是明智的。请注意,我有一个对fi.get()的调用,它将阻塞主线程,直到异步线程满足未来。

更新代码:

#include <cmath>
#include <iostream>
#include <thread>
#include <future>
#include <chrono>
#include <functional>
#include <atomic>

// provide a means of emitting to stdout without a race condition
std::mutex emit_mutex;
template<class...Ts> void emit(Ts&&...ts)
{
  auto lock = std::unique_lock<std::mutex>(emit_mutex);
  using expand = int[];
  void(expand{
    0,
    ((std::cout << ts), 0)...
  });
}

// cross-thread communications are UB unless either:
// a. they are through an atomic
// b. there is a memory fence operation in both threads
//    (e.g. condition_variable)
int func(std::atomic<bool>& on)
{
    while(on) {
        auto t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
        emit(ctime(&t), "\n");
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    return 6;
}

int main()
{
  std::atomic<bool> on { true };
  std::future<int> fi = std::async(std::launch::async, func, std::ref(on));
  std::this_thread::sleep_for(std::chrono::seconds(5));
  on = false;
  emit("function returned ", fi.get(), "\n");
  return 0;
}

输出示例:

Wed Jun 22 09:50:58 2016

Wed Jun 22 09:50:59 2016

Wed Jun 22 09:51:00 2016

Wed Jun 22 09:51:01 2016

Wed Jun 22 09:51:02 2016

function returned 6

根据要求,对发射进行解释

template<class...Ts> void emit(Ts&&...ts)

emit是一个函数,它返回val并通过x值引用(即const ref、ref或r-value ref)获取任意数量的参数。即它将接受任何东西。这意味着我们可以调用:

>

  • emit(foo())-使用函数的返回值(r-value)进行调用

    emit(x,y,foo(),bar(),“text”)-使用两个引用、两个r-value-references和一个字符串文本调用

    使用扩展=int[];将类型定义为长度不确定的整数数组。当我们实例化扩展类型的对象时,我们将仅使用它来强制计算表达式。实际的数组本身将被优化器丢弃——我们只是想要构造它的副作用。

    void(展开{…}) -强制编译器实例化数组,但void cast告诉它,我们永远不会使用实际的数组本身。

    ((标准::cout

    <代码>0,-数组的第一个元素为零。这就满足了有人在没有参数的情况下调用emit()的情况。参数包Ts将为空。如果没有这个前导零,数组的结果求值将是int[]{},这是一个长度为零的数组,在c中是非法的。

    初学者注意事项:

    数组的初始化器列表中的所有内容都是表达式。

    表达式是一系列产生某些对象的“算术”操作。该对象可能是实际对象(类实例)、指针、引用或整数等基本类型。

    所以在这种情况下,d::c

    将表达式置于括号中不会改变其含义。它只会强制进行排序。e、 g.<代码>a

    逗号“,”也是一个运算符a(),b()表示“调用函数a,然后放弃结果,然后调用函数b。返回的值应为b返回的值”。

    所以,通过一些心理训练,你应该能够看到

    所以当Ts是(比如说)一个int和一个字符串指针时,Ts...将是这样的打字员

    因此表达式将扩展为:

    void(int[] {
    0,
    ((std::cout << x), 0),
    ((std::cout << "Hello world"), 0),
    });
    

    这在婴儿阶段意味着:

    1. 分配长度为3的数组

    当然,优化器看到这个数组从未被使用过(因为我们没有给它起一个名字),所以它删除了所有不必要的位(因为优化器就是这么做的),它变得相当于:

    1. 调用std::cout

  •  类似资料:
    • 我已经写了这个生产者/消费者问题解决方案。它似乎在工作,而不是无限循环。我的印象是,pthread\u exit(NULL) 会让它停止,但老实说,我已经迷路了。有人能告诉我如何阻止循环的正确方向吗?

    • 问题内容: 由于某种原因,当我在路由中拥有动态属性并访问该页面时,我陷入了无限循环,该页面将不断请求自己。 我正在查看的路由是/ userlists /:id路由。该路由的控制器是- 当我访问/ userlists / 9时,我看到- 每3秒被记录一次,页面冻结。只要位置后面有一个斜杠(“ / userslists /”而不是“ / userlists”),就好像发生了这种情况。 有人知道原因吗?

    • 正在学习一些关于多线程的教程,并编写了一些代码来创建一个无限线程。然而,无限循环似乎不起作用。循环内的打印例程只运行一次。找到下面的代码以供参考。不明白我做错了什么。我正在使用MSVC编译器。

    • 我需要编写一个扩展Thread类的应用程序。我的类在实例化时接受一个整数(即100)<代码>(MyThread myt=新的MyThread(100);) 这个整数将是这个类循环并打印消息的次数。消息应该是“线程正在运行…100”。100将是我传入构造函数的任何数字。如果数字是150,那么输出应该是“线程正在运行…100”。我应该使用main方法来测试这个类。在主线程中,我将启动两个线程,一个线程

    • 问题内容: 我正在使用从队列中连续读取的线程。 就像是: 停止此线程的最佳方法是什么? 我看到两个选择: 1-由于已弃用,因此我可以实现使用原子检查条件变量的方法。 2-将死亡事件对象或类似的对象发送到队列。当线程获取死亡事件时,它退出。 我更喜欢第一种方法,但是,我不知道何时调用该方法,因为可能有某种方法进入队列,并且停止信号可能首先到达(这是不希望的)。 有什么建议? 问题答案: 该(或因为它

    • 问题内容: 我用Java 8编写了一个服务器应用程序,并使用Java 1.8.0u25运行它。 它可以在最初的几个小时内正常工作,但是在收到大约5k至10k请求之后,VM进程的线程使用了其中一个CPU的100%。 因此,我尝试让VM进程检查有问题的线程是什么,它显示线程(线程ID为14303 = 0x37df)为“ C2 CompilerThread0”: 使用,线程的堆栈跟踪如下: 每一次我尝试