2009-02-19 210 views
31

我需要在交互式应用程序中管理CPU繁重的多任务作业。正如背景一样,我的具体应用是工程设计界面。当用户调整模型的不同参数和选项时,多个模拟会在后台运行,并在结束时显示结果,甚至可能在用户仍在编辑值时发生。由于多个模拟需要可变时间(有些是毫秒,有些需要5秒,有些需要10分钟),但基本上是尽可能快地获得反馈,但通常会中止以前开始但现在不再需要的作业,因为用户的更改已使其失效。不同的用户更改可能会使不同的计算失效,因此我可能随时运行10个不同的模拟。有些模拟有多个部分有相关性(模拟A和B可以单独计算,但我需要他们的结果来种子模拟C,因此我需要等待A和B在开始C之前先完成)。多线程作业队列管理器

我觉得非常确信处理这种应用程序的代码级方法是某种多线程作业队列。这包括提交执行作业,设置任务优先级,等待作业完成,指定依赖关系(完成此作业,但仅在作业X和作业Y完成后),取消符合某些标准的作业子集,查询什么内容工作依然存在,设置工作者线程数和优先级等等。多平台支持也非常有用。

这些并不是新的想法或软件的愿望,但我在我的应用程序的早期设计阶段,我需要选择哪些库用于管理这些任务。我过去曾在C写过自己的粗线程经理(我认为这是一段经文),但我想用现代工具来开展工作,而不是我以前的黑客。

第一个想法是运行到OpenMP,但我不确定这是我想要的。 OpenMP非常适合在良好的级别进行并行处理,自动展开循环等。在多平台的同时,它也通过#pragmas侵入你的代码。但大多数情况下,它不是为管理大型任务而设计的,特别是取消挂起的作业或指定依赖关系。可能的,是的,但它并不优雅。

我注意到Google Chrome uses such a job manager for even the most trivial tasks.设计目标似乎是让用户交互线程尽可能轻便灵活,所以任何可以异步生成的东西都应该是这样。从查看Chrome源代码看,这似乎不是一个通用库,但看到设计如何使用异步启动来保持快速交互仍然很有趣。这与我正在做的事情类似。

还有一个另外的选择:

Surge.Act:定义工作升压样库。它建立在OpenMP之上,但确实允许链接依赖关系,这很好。看起来似乎没有一个经理可以被查询,工作被取消等。这是一个过时的项目,所以它依赖它是可怕的。

Job Queue非常接近我的想法,但它是一个5年的文章,而不是一个支持的库。

Boost.threads确实有很好的平台独立同步,但这不是一个工作管理器。 POCO具有非常干净的任务启动设计,但又不是完整的经理链接任务。 (也许我低估了POCO)。

所以虽然有可用的选项,我不满意,我觉得再次推出自己的图书馆的冲动。但我宁愿使用已经存在的东西。即使在搜索过程中(在SO和网上),我还没有发现任何感觉正确的东西,但我想这应该是一种经常需要的工具,所以肯定有一些社区库或至少是常见的设计。 在SO有postsjob queues,但似乎不适合。

我在这里的帖子是问你所有我已经错过的工具,和/或你如何推出自己的这种多线程作业队列。

回答

5

我推出了自己的,基于Boost.threads。我很惊讶我从写这么小的代码得到多少回报。如果你没有找到预先制作的东西,不要害怕推出自己的产品。在Boost.threads和写自己的经验之间,这可能比你想象的要容易。

对于预制选项,不要忘记,Chromium被许可非常友好,所以您可能能够围绕其代码推出自己的通用库。

1

看看boost::future(也看到这个discussionproposal),它看起来像并行了一个非常好的基础(尤其是它似乎提供了良好的支持C-取决于-ON-A-和B型的情况下)。

我看了一下OpenMP,但是(像你一样)不相信它对除Fortran/C数字代码以外的其他任何东西都能很好地工作。英特尔的Threading Building Blocks看起来更有趣。

如果说到这一点,在boost :: thread之上对roll your own不是太难。 [说明:线程farm(大多数人会称之为池)从线程安全的函子(任务或作业)queue中提取工作。有关使用示例,请参阅testsbenchmark。我有一些额外的复杂功能来(可选)支持具有优先级的任务,以及执行任务可以在工作队列中产生更多任务的情况(这使得知道何时所有工作实际上完成了更多问题;引用“正在等待”是可以处理案件的)。无论如何可以给你一些想法。]

+0

优秀。 boost :: future看起来与POCO的ActiveResults非常相似(http://pocoproject.org/poco/docs/Poco.ActiveResult.html)。再说,这不是一个工作队列管理员,但仍然是一个很棒的工具。 Boost:线程的基础确实感觉像是制作自定义管理器的最佳低级工具包。 – 2009-02-20 01:23:54

3

会像threadpool对你有用吗?它基于boost :: threads,基本实现了一个简单的线程任务队列,将工作函数传递给池线程。

2

您可能想看看 Flow-Based Programming - 它基于异步组件之间的数据块流式传输。有驱动程序的Java和C#版本,以及一些预编码组件。它本质上是多线程的 - 实际上唯一的单线程代码是在组件内部,尽管您可以将时序约束添加到标准调度规则中。虽然它可能处于您需要的级别太高的级别,但您可以使用这些内容。

17

我们必须建立我们自己的作业队列系统,以满足与你相似的要求(UI线程必须总是在为33ms响应,作业可以从15-15000ms运行),因为实在没有什么东西可以满足我们的需求,更不用说高性能。

不幸的是我们的代码是关于专有专有得到,但我可以给你一些最显着的特征:

  • 我们在节目的一开始启动每个核心一个线程。每个人都从全球工作队列中抽取工作。作业由一个函数对象和一组相关数据组成(真的是一个func_ptr和void *的详细说明)。线程0,快速客户端循环,不允许在作业上工作,但其余的尽可能抓取。
  • 作业队列本身应该是无锁数据结构,例如lock-free singly linked list(Visual Studio comes with one)。避免使用互斥体;对队列的争夺惊人地高,并且抢互斥是昂贵的。
  • 将作业的所有必要数据打包到作业对象本身 - 避免将作业的指针返回到主堆中,必须处理作业与锁之间的争用以及所有其他缓慢,烦人的东西。例如,所有的模拟参数都应该放入作业的本地数据blob中。结果结构显然需要是超出工作的东西:你可以通过以下方式来处理这个问题:a)挂起作业对象,即使它们已经完成运行(因此可以使用主线程中的内容),或者b)为每个作业分配一个结果结构,并将指针填充到作业的数据对象中。即使结果本身不会在工作中生效,这可以有效地让工作对其输出内存进行独占访问,因此您不必担心会出现锁定问题。

  • 实际上我简化了一点以上,因为我们需要精确地编写哪些作业在哪些内核上运行,所以每个内核都有自己的作业队列,但这可能对您没有必要。

+0

你可以介绍一下“精确地指出哪些作业在哪些内核上运行,因此每个内核都有自己的作业队列”,这是什么原因,它是否与处理器关联有关(https://en.wikipedia.org/wiki/Processor_affinity)? – athos 2017-01-24 00:55:03

+1

@athos这是另一种方式:处理器亲和力是我们如何说哪​​个作业在哪个核心上运行。我们需要精确地编排事物,以便两个实时线程能够在其核心上持续运行而不会有任何抢占风险,而其他作业则以最大化缓存位置,超线程效率等方式分配给特定硬件核心。一个固定的硬件平台可以非常紧密地优化。 – Crashworks 2017-03-23 21:51:23

4

微软正在为Visual Studio 2010的下一个版本开发一组称为并发运行时,并行模式库和异步代理库的技术,这可能会有所帮助。并发运行时将提供基于策略的调度,即允许您管理和组合多个调度程序实例(类似于线程池,但实例之间具有关联和负载平衡),并行模式库将提供基于任务的编程和带STL的并行循环编程模型。代理程序库提供基于代理程序的编程模型,并支持构建并发数据流管道,即管理上述依赖关系。不幸的是,这还没有发布,所以你可以在我们的team blog上看到它,或者观看一些视频on channel9,还有一个非常大的CTP可供下载。

如果您现在正在寻找解决方案,英特尔的线程构建模块和boost的线程库都是很好的库,现在就可以使用。 JustSoftwareSolutions已发布std :: thread的实现,该实现与C++ 0x草稿相匹配,当然,如果您正在寻找基于细粒度循环的并行机制,则OpenMP是广泛可用的。

其他人已经提到的真正挑战是正确识别并将工作分解为适合并发执行的任务(即没有不受保护的共享状态),理解它们之间的依赖关系并最小化可能发生在瓶颈上的争用(无论是瓶颈是protecting shared state或确保工作队列的调度循环是低争用或无锁)......并且在没有安排实现细节泄露到代码的其余部分的情况下执行此操作。

-Rick

1

有很多分布式资源管理器。几乎满足您所有要求的软件是Sun Grid Engine。 SGE被用于一些世界上最大的超级计算机,并且正在积极开发中。

Torque,Platform LSFCondor中也有类似的解决方案。

听起来你可能想要推出自己的产品,但上述所有功能都有很多。