c++ - 如何向 std::thread 发出信号以优雅退出?

标签 c++ multithreading c++17

使用 C++17,对于其中包含执行某些任务的非阻塞循环的工作线程,我看到三种方法来通知线程退出:

  1. 一个std::atomic_bool线程在循环中进行检查。如果设置为true ,线程退出。主线程将其设置为 true在调用std::thread::join()之前.
  2. 一个std::condition_variablebool 。这与上面类似,只是它允许您调用 std::condition_variable::wait_for()在等待潜在的退出信号时有效地“ sleep ”线程(以降低 CPU 使用率)(通过设置 bool ,在 wait_for() (谓词)的第三个参数中检查该值。主线程将锁定一个互斥锁,将 bool 值更改为 true ,并在调用 std::condition_variable::notify_all() 之前调用 std::thread::join() 以通知线程退出。
  3. 一个std::futurestd::promise 。主线程持有 std::promise<void>而工作线程持有相应的std::future<void> 。工作线程使用std::future::wait_for()与上面的步骤类似。主线程调用std::promise::set_value()在调用std::thread::join()之前.

我对每个问题的想法:

  1. 这很简单,但缺乏在不显式调用 std::this_thread::sleep_for() 的情况下“减慢”工作线程循环的能力。 。看起来像是一种“老式”的线程信号处理方式。
  2. 这个很全面,但是非常复杂,因为您需要一个条件变量加上一个 bool 变量。
  3. 这似乎是最好的选择,因为它具有#1 的简单性,但没有#2 的冗长。但我对std::future没有个人经验和std::promise然而,所以我不确定这是否是理想的解决方案。在我看来,promise 和 future 的目的是跨线程传输值,而不是真正用作信号。所以我不确定是否存在效率问题。

我看到有多种方式来通知线程退出。可悲的是,随着我不断寻找,我的 Google 搜索仅引入了更多内容,而实际上并未就使用 C++17 执行此操作的“现代”和/或“最佳”方法达成普遍共识。

我很想看到一些关于这种困惑的线索。有没有一个决定性的、明确的方法来做到这一点?普遍的共识是什么?如果没有“一刀切”的话,每种解决方案的优缺点是什么?

最佳答案

如果您有一个繁忙的工作线程,需要单向通知(如果它应该停止工作),最好的方法就是使用 atomic<bool> 。如果工作线程想放慢速度或不想放慢速度,则取决于工作线程。 “限制”工作线程的要求与线程取消完全正交,在我看来,不应将其与取消本身一起考虑。据我所知,这种方法有两个缺点:无法传回结果(如果有),并且无法传回异常(如果有)。但如果您不需要其中任何一个,请使用 atomic<bool>并且不用担心其他任何事情。它和其他任何东西一样现代;没有什么过时的。

condition_variable是消费者/生产者模式的一部分。因此,有些东西会产生工作,有些东西会消耗所生产的东西。避免忙于等待消费者而没有任何东西可以消费 condition_variable是一个很好的选择。它是此类任务的完美原语。但对于线程取消过程来说没有意义。无论如何,您都必须使用另一个变量,因为您不能依赖 condition_variable独自的。它可能会虚假地唤醒线程。您可能会在它进入等待过程之前对其进行“设置”,从而完全丢失“设置”,等等。它不能单独使用,所以我们回到第一个方向,但现在有了 atomic<bool>变量伴随我们condition_variable

future/promise当您需要知道在另一个线程上完成的操作的结果时,pair 是很好的选择。因此,它不能替代 atomic<bool> 的方法。但它是对它的补充。因此,要消除第一段中描述的缺点,您添加 future/promise到方程。您向调用方提供 future摘自promise它存在于线程内。那promise线程完成后设置:

  • 因为抛出了异常。
  • 因为线程已经完成了它的工作并自行完成。
  • 因为我们通过设置 atomic<bool> 要求它停止多变的。

如您所见 future/promise对只是帮助为被调用者提供一些反馈,它与取消本身无关。

附注您始终可以使用电动大锤来敲碎坚果,但这并不会使该方法变得更加现代。

关于c++ - 如何向 std::thread 发出信号以优雅退出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57728255/

相关文章:

java - ReentrantLock.lock() 不会阻塞其他线程

c++ - 使用c++ 14构建时可以使用pack fold表达式(c++ 17扩展名)

c++ - 在 C++17 中弃用 `std::result_of` 的原因是什么?

c++ - 如何简化模板模板参数中的enable_if别名

c++ - 为什么operator() with type argument可以应用于result_of上下文中的type?

c++ - boost::asio::io_service 的 concurrency_hint 是什么意思?

c++ - 如何为所有派生类型部分特化类模板?

c++ - 将(n 个第一个字节的)unsigned char 指针转换为 float 和 double C++

Python 多处理信号量不工作

c++ - 如何打印 C++ 应用程序中所有线程的列表?