【问题标题】:Is there a way to find out, whether a thread is blocked?有没有办法找出线程是否被阻塞?
【发布时间】:2013-07-03 09:36:35
【问题描述】:

我正在用 C++ 编写一个线程池类,它接收要并行执行的任务。如果可能的话,我希望所有内核都处于忙碌状态,但有时某些线程是空闲的,因为它们出于同步目的而被阻塞了一段时间。发生这种情况时,我想启动一个新线程,以便始终有大约与 cpu 内核一样多的线程处于唤醒状态。为此,我需要一种方法来确定某个线程是唤醒还是休眠(阻塞)。我怎样才能找到这个?

出于可移植性目的,我更喜欢使用 C++11 标准库或 boost。但如有必要,我也会使用 WinAPI。我在 Windows 7 上使用 Visual Studio 2012。但实际上,我希望有一种可移植的方式来执行此操作。

最好这个线程池应该能够掌握这样的情况

MyThreadPool pool;
for ( int i = 0; i < 100; ++i )
    pool.addTask( &block_until_this_function_has_been_called_a_hundred_times );
pool.join(); // waits until all tasks have been dispatched.

函数block_until_this_function_has_been_called_a_hundred_times() 阻塞,直到100 个线程调用它。此时所有线程都应该继续运行。线程池的一个要求是它不应该因为池中的线程数量太少而死锁。

【问题讨论】:

  • 我想你可以测量当时的 CPU 使用率。但是除非你的阻塞真的很长(如果你有一个锁的包装器,你可以保持“我睡多久”的统计信息),那么启动一个新线程可能需要比线程被阻塞更长的时间。我不知道这样做的任何标准方式。
  • 屏蔽是什么意思?如果您的某些线程因为被阻塞而处于空闲状态,那么启动另一个线程有什么帮助,它不会也空闲等待与现有线程相同的资源吗?正如@MatsPetersson 所说,您可以测量线程的空闲时间,但也许您需要测量资源的吞吐量以保持线程尽可能忙碌。
  • 何必呢?只要确保池或其他任何东西有足够数量的额外线程即可。
  • @RalphTandetzky 试试吧。您在 OP 中建议的是通过在用户代码中执行操作系统已经执行的操作来微观管理线程。当正在运行的线程被阻塞时,操作系统非常擅长将控制权转移到另一个线程。你的计划几乎肯定会适得其反。仅当 READY 线程通常比内核多且这些线程使用过多的 L1 缓存以致缓存刷新变得昂贵时,上下文切换开销才会成为问题。
  • 您可以使用英特尔 VTune 等工具测量线程在同步时被阻塞的时间量。但是@dwxw 的问题仍然存在:如果您的线程被阻止竞争 std::mutexes 或 Windows 关键部分,那么 that 就是您的问题。使用更细粒度的同步,(或者如果是那种程序,则修复您的负载平衡)。另外:正如 Martin James 指出的那样,线程数是 CPU 内核的 2 倍或 3 倍(甚至 4 倍),您将永远注意到上下文切换开销。操作系统调度程序对此太好了。

标签: c++ multithreading c++11 threadpool blocking


【解决方案1】:

为您的线程池添加一个工具,让线程说“我被阻止了”,然后说“我不再被阻止了”。在每个重要的阻止操作之前(请参阅下文了解我的意思)发出“我被阻止”的信号,然后是“我不再被阻止”。

什么构成“重大阻止行动”?当然不是简单的互斥锁:互斥锁应该只持有很短的时间,所以阻塞互斥锁不是什么大问题。我的意思是:

  • 等待 I/O 完成
  • 正在等待另一个池任务完成
  • 等待共享队列中的数据

和其他类似的事件。

【讨论】:

  • 感谢您让我的回答更加完整和结构化 :)
【解决方案2】:

使用Boost Asio。它有自己的线程池管理和调度框架。基本思想是使用post() 方法将任务推送到io_service 对象,并从与您拥有的CPU 内核一样多的线程调用run()。您应该在计算运行时创建一个work 对象,以避免线程在没有足够作业时退出。

关于 Asio 的重要一点是永远不要使用任何阻塞调用。对于 I/O 调用,使用 Asio 自己的 I/O 对象的异步调用。对于同步,请使用 strand 对象而不是互斥锁。如果您将函数发布到包装在一个链中的 io 服务,那么它可以确保在任何时候最多运行一个属于某个链的任务。如果发生冲突,任务将保留在 Asio 的事件队列中,而不是阻塞工作线程。

虽然使用异步编程有一个缺点。阅读分散在多个异步调用中的代码比阅读具有清晰控制流的代码要困难得多。在设计程序时,您应该意识到这一点。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-27
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    相关资源
    最近更新 更多