【发布时间】: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