并发::parallel_fo(PPL)创建的线程太多

Concurrency::parallel_for (PPL) is creating too many threads

本文关键字:创建 太多 线程 parallel fo 并发 PPL      更新时间:2023-10-16

我使用Visual Studio 2010的并行模式库(PPL)的Concurrency::parallel_for()来处理一组索引任务(通常,索引集远大于可以同时运行的线程数)。在进行长时间计算之前,每个任务都会从共享资源管理器请求一个专用的工作存储资源开始(在这种情况下:一个特定于任务的内存映射文件的视图,但我认为如果每个任务都从共享堆请求一个私有内存分配,情况会是一样的)。

共享资源管理器的使用与Concurrency::critical_section同步,问题就从这里开始了:如果第一个线程/任务在关键部分,而第二个任务发出请求,它必须等到第一个任务的请求得到处理。PPL显然认为:嘿,这个线程正在等待,还有更多的任务要做,因此创建了另一个线程,导致多达870个线程在同一资源管理器中等待。

现在,由于处理资源请求只是整个任务的一小部分,我想告诉该部分的PPL要稳住阵脚,任何等待或协作块都不应该导致新线程从工作线程的指示部分开始,我的问题是:如果以及如何阻止特定线程部分创建新线程,即使它协同阻止。我不介意在线程处理路径的其他块上创建新线程,但也不介意超过2*(超)核的数量。

到目前为止,我已经考虑过的替代方案:

  1. 将任务排队,并从有限数量的线程中处理队列。问题:我希望PPL的parallel_fo能自己做到这一点。

  2. 定义一个Concurrency::combinable<Resource> resourceSet;在CCD_ 4外部初始化CCD_。问题:这种优化并不能阻止多余的线程创建。

  3. parallel_for循环之外的每个任务预先分配所需的资源。问题:这会请求太多的系统资源,而将资源量限制在线程/内核的数量是可以的(如果没有爆炸的话)。

我读过http://msdn.microsoft.com/en-us/library/ff601930.aspx,部分"不要在并行循环中重复阻塞",但遵循这里的建议将导致根本没有并行线程。

我不知道是否可以将PPL/ConcRT配置为不使用协作同步,或者至少限制它创建的线程数量。我认为它可能通过调度器策略来控制,但似乎没有一个策略参数适合这个目的。

然而,我有一些建议,你可能会发现有助于缓解问题,即使不是以理想的方式:

  • 使用非协作同步原语来保护资源管理器,而不是critical_section。我认为(尽管没有检查)经典的WinAPI CRITICAL_SECTION应该会成功。作为朝着这个方向迈出的重要一步,您可以考虑为您的代码使用其他并行库;例如,Intel的TBB提供了大部分PPL API,并提供了更多(免责声明:我隶属于它)。

  • 在并行循环之外预先分配一些资源。每个任务不需要一个资源;每个线程一个就足够了。将这些资源放入concurrent_queue中,然后在任务内部从队列中弹出一个资源,使用,然后将其推回。此外,线程可能会将资源囤积在combinable对象中,以便在其他任务中重用,而不是将资源返回到队列。如果队列碰巧是空的(例如,如果PPL超额订阅了机器),可能会有不同的方法,例如在循环中旋转,直到某个其他线程返回资源,或者向管理器请求另一个资源。此外,您可以选择预先分配比线程数量更多的资源,以最大限度地减少资源耗尽的机会。

我的答案不是使用PPL的"解决方案",但我认为使用像taskqueue这样的线程池可以很容易地做到这一点,你应该看看这个答案。

所以你用你的作品填满了队列,它确保了不会有超过"x"个任务并行工作,其中x是boost::thread::hardware_concurrency()(是的,再次提升…)