如何向 std::thread 发出信号以正常退出
How do I signal a std::thread to exit gracefully?
使用 C++17,对于具有执行某些任务的非阻塞循环的工作线程,我看到三种指示线程退出的方法:
- 线程在循环中检查的
std::atomic_bool
。如果设置为true
,线程将退出。主线程在调用std::thread::join()
之前将其设置为true
。 - 一个
bool
的std::condition_variable
。这与上述类似,除了它允许您调用std::condition_variable::wait_for()
以有效地"休眠"线程(以降低 CPU 使用率),同时等待潜在的退出信号(通过设置bool
,在第三个参数中检查wait_for()
(谓词)。主线程将锁定互斥锁,将 bool 更改为true
,并在调用std::thread::join()
之前调用std::condition_variable::notify_all()
以指示线程退出。 std::future
和std::promise
.主线程保存std::promise<void>
,而工作线程保存相应的std::future<void>
。工作线程使用类似于上述步骤的std::future::wait_for()
。主线程在调用std::thread::join()
之前调用std::promise::set_value()
。
我对每个想法:
- 这很简单,但缺乏在不显式调用
std::this_thread::sleep_for()
的情况下"减慢"工作线程循环的能力。似乎是执行线程信号的"老式"方式。 - 这个很全面,但非常复杂,因为你需要一个条件变量和一个布尔变量。
- 这似乎是最好的选择,因为它具有#1的简单性而没有#2的冗长。但我还没有
std::future
和std::promise
的个人经验,所以我不确定这是否是理想的解决方案。在我看来,promise&future旨在跨线程传递价值,而不是真正用作信号。所以我不确定是否存在效率问题。
我看到多种方式可以发出线程退出的信号。可悲的是,随着我一直在寻找,我的谷歌搜索只是引入了更多,并没有真正就C++17的"现代"和/或"最佳"方式达成共识。
我很想看到一些关于这种困惑的曙光。有没有一个决定性的、确定的方法可以做到这一点?普遍共识是什么?如果没有"一刀切",每种解决方案的优缺点是什么?
如果您有一个繁忙的工作线程,需要单向通知,如果它应该停止工作,最好的方法是只使用atomic<bool>
。这取决于工作线程是否想要减慢速度或不想减慢速度。"限制"工作线程的要求与线程取消完全正交,在我看来,不应与取消本身一起考虑。据我所知,这种方法有两个缺点:您无法传回结果(如果有),也无法传回异常(如果有)。但是,如果您不需要其中任何一个,请使用atomic<bool>
,不要打扰其他任何事情。它和任何一样现代;没有什么过时的
。condition_variable
是消费者/生产者模式的一部分。所以有些东西可以产生工作,有些东西会消耗生产的东西。为了避免在没有什么可消费的情况下忙于等待消费者,condition_variable
是一个很好的选择。它只是此类任务的完美原语。但这对线程取消过程没有意义。无论如何,您都必须使用另一个变量,因为您不能单独依赖condition_variable
。它可能会虚假地唤醒线程。您可能会在它进入等待过程之前"设置"它,完全丢失"设置",依此类推。它不能单独使用,所以我们回到原点,但现在有一个atomic<bool>
变量来陪伴我们的condition_variable
当您需要知道在另一个线程上完成的操作的结果时,future
/promise
对是很好的。因此,它不是用atomic<bool>
取代方法,而是补充它。因此,为了消除第一段中描述的缺点,您在等式中添加了future
/promise
。您向调用方提供从线程中存在的promise
中提取的future
。线程完成后,将设置该promise
:
- 因为抛出异常。
- 因为线程已经完成了它的工作并自行完成。
- 因为我们要求它通过设置
atomic<bool>
变量来停止。
因此,如您所见,future
/promise
对只是有助于为被叫方提供一些反馈,这与取消本身无关。
附言您始终可以使用电动大锤来敲碎螺母,但这并不能使这种方法更加现代。
我不能说这是决定性的或确定性的,但由于这在某种程度上是一个意见问题,我会给出一个答案,它是基于大量的试验和错误来解决你问的那种问题(我认为)。
我的首选模式是使用原子布尔值向线程发出停止信号,并使用条件变量控制"循环"计时。
我们经常遇到在工作线程上运行重复任务的要求,因此我们创建了一个称为"threaded_worker"的类。 此类处理中止线程和计时对辅助角色函数的调用的复杂性。
中止是通过设置原子布尔值"abort"信号的方法处理的,该方法告诉线程停止调用工作函数并终止。
循环计时可以通过设置条件变量等待时间的方法进行控制。 可以通过调用条件变量上的通知的方法释放线程以继续。
我们将该类用作各种对象的基类,这些对象具有需要在单独的线程上执行的某些函数。 该类旨在运行一次或循环运行"work"函数。
我们使用布尔值进行中止,因为它简单且适合完成工作。 我们使用条件变量进行循环定时,因为它的好处是被通知"短路"时序。 当线程对象是使用者时,这非常有用。 当生产者对线程对象有工作时,它可以对工作进行排队并通知工作可用。 线程对象立即继续,而不是等待条件变量的指定等待时间。
两者(中止信号和条件变量)的原因是,我认为终止线程是一个函数,而将循环计时为另一个函数。
我们过去常常通过将线程置于睡眠状态一段时间来计时循环。 这使得在Windows计算机上几乎不可能获得可预测的循环计时。 有些计算机将在大约 1 毫秒后从睡眠(1)返回,但其他计算机将在 15 毫秒后返回。 我们的性能高度依赖于特定的硬件。 使用条件变量,我们大大改进了关键任务的时间安排。 在工作可用时通知等待线程的额外好处是值得条件变量的复杂性。
- 为什么"do while"循环不断退出,即使条件计算结果为 false?
- 创建LinkedList退出,返回代码为-11(SIGSEGV)
- 当我在main中声明了我的2d数组时,为什么我的程序会退出
- 如何让LLDB在成功时退出,在失败时等待
- C++控制台应用程序阻止退出
- 程序在执行程序的其余部分之前退出
- 构造函数在退出函数时无法初始化一个参数
- 为什么异常不退出程序?
- 我不断收到 [错误] ID 返回 1 退出状态错误,但看不到问题所在
- 退出简单while循环时出现问题
- 使用vscode调试时,GDB意外退出
- pclose() 不会给我进程退出代码
- 为什么系统函数总是在C++中返回已转移的退出状态?
- C++从另一个函数退出函数
- C++ 中的编译错误:未定义对"主"的引用 collect2:错误:ld 返回 1 个退出状态
- C++逗号分隔的输入数组代码过早退出
- Netbeans 10:错误:链接器命令失败,退出代码为 1(使用 -v 查看调用)
- 为什么 C++ 中的以下结构声明会导致退出 127?
- 编译问题:在函数"_start"中:未定义对"主"的引用 collect2:错误:ld 返回 1 个退出状态
- 进程退出,返回值3221226356写入系统( "cls" )。(已解决)