繁体   English   中英

如何发信号通知std :: thread优雅地退出?

[英]How do I signal a std::thread to exit gracefully?

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

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

我对每个人的看法:

  1. 这很简单,但缺乏“减速”工作线程循环而不显式调用std::this_thread::sleep_for() 似乎是一种做旧线程信号的“老式”方式。
  2. 这个是全面的,但非常复杂,因为你需要一个条件变量加上一个布尔变量。
  3. 这个似乎是最好的选择,因为它具有#1的简单性而没有#2的冗长。 但是我对std::futurestd::promise没有个人经验,所以我不确定它是否是理想的解决方案。 在我看来,承诺和未来意味着跨线程传递值,而不是真正用作信号。 所以我不确定是否存在效率问题。

我看到多种方式发信号通知线程退出。 可悲的是,我的谷歌搜索只引入了更多,因为我一直在寻找,而实际上并没有就使用C ++ 17的“现代”和/或“最佳”方式达成一致意见。

我很想看到这种混乱的一些亮点。 这样做是否有确凿的,明确的方式? 普遍的共识是什么? 如果没有“一刀切”的解决方案,每种解决方案的优缺点是什么?

如果你有一个繁忙的工作线程需要单向通知,如果它应该停止工作,最好的方法是使用atomic<bool> 如果工作线程要减速或者不想减速,则由工作线程决定。 “限制”工作线程的要求与线程取消完全正交,并且在我看来,不应该考虑取消本身。 据我所知,这种方法有两个缺点:你不能传回结果(如果有的话)而且你不能传回异常(如果有的话)。 但是,如果你不需要任何这些,那么使用atomic<bool>并且不要为其他任何事情烦恼。 它和任何现代一样现代; 关于它没有什么老式的。

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

当您需要知道在另一个线程上完成的操作的结果时, future / promise对很好。 所以它不是atomic<bool>的替代方法,而是它的补充。 因此,为了消除第一段中描述的缺点,您可以在等式中添加future / promise 您向主叫方提供从线程中的promise中提取的future 一旦线程完成,该promise就会被设置:

  • 因为异常被抛出。
  • 因为线程已完成其工作并自行完成。
  • 因为我们通过设置atomic<bool>变量让它停止。

因此,当您看到future / promise对有助于为被调用者提供一些反馈时,它与取消本身无关。

PS你可以随时使用电动大锤来破解螺母,但这并不能使这种方法更加现代化。

我不能说这是确凿的,或者是确定的,但由于这有点是一个意见问题,我会给出一个答案,它是基于大量的试验和错误来解决你所问的那种问题(我认为)。

我首选的模式是通知线程停止使用原子bool,并使用条件变量控制'循环'时序。

我们遇到了在工作线程上运行重复任务的要求,因此我们经常创建一个名为'threaded_worker'的类。 此类处理中止线程的复杂性,以及对worker函数的调用计时。

中止是通过一个方法处理的,该方法设置原子bool'abort'信号,该信号告诉线程停止调用工作函数并终止。

循环定时可以通过设置条件变量的等待时间的方法来控制。 可以通过调用条件变量上的notify的方法释放线程以继续。

我们将该类用作所有类型对象的基类,这些对象具有需要在单独线程上执行的某些函数。 该类旨在运行“work”函数一次或循环运行。

我们使用bool进行中止,因为它很简单,适合做这项工作。 我们将条件变量用于循环定时,因为它具有通知“短路”时序的好处。 当线程对象是使用者时,这非常有用。 当生产者为线程对象工作时,它可以对工作进行排队并通知工作可用。 线程对象立即继续,而不是等待条件变量的指定等待时间。

(中止信号和条件变量)的原因是我看到将线程终止为一个函数,并将循环定时为另一个函数。

我们过去通过让线程休眠一段时间来计时循环。 这使得几乎不可能在Windows计算机上获得可预测的循环时序。 有些计算机将在大约1ms内从睡眠状态(1)返回,但其他计算机将在15ms内返回。 我们的性能高度依赖于特定的硬件。 使用条件变量,我们大大改善了关键任务的时间安排。 当工作可用时通知等待线程的额外好处是值得条件变量的复杂性。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM