强制 TPL 任务在单核上 运行

Force TPL Tasks to run on a single core

我有一个 ETL 项目,其中有一些处理组件。单体组件是基于 BlockingCollection 的生产者-消费者。所有组件都通过 Task.Run 并行执行,等待项目从其他组件到达,处理它们并将结果放入它们的输出集合(想想管道)。所有组件都通过 Task.Run().

执行

是否可以强制任务在单核上 运行(我不希望它们占用 100% 的多核 CPU)而不为进程设置处理器亲和力(这似乎有点矫枉过正)?

请注意,我仍然希望任务以并行方式 运行 - 仅在单个内核上。

一个任务在一个线程上执行,OS决定它在哪个内核上执行。

我认为除了设置 Processor Affinity 之外没有其他方法。

看这里:https://msdn.microsoft.com/en-us/library/system.diagnostics.processthread.processoraffinity.aspx

您确定 运行 它们在一个内核上的并行将提高性能,为什么您不想让进程在需要时潜在地使用 100% cpu? os 仍会优先考虑其他进程,而不是 necceserily 允许此

如果您担心您的进程对其他 OS 进程造成压力,您也可以降低 Thread/Process 优先级:

进程优先级:https://msdn.microsoft.com/en-us/library/system.diagnostics.process.priorityclass.aspx 线程优先级:https://msdn.microsoft.com/en-us/library/system.threading.thread.priority(v=vs.110).aspx

是的,这完全有可能。您只需要实现自己的 TaskScheduler.

事实上,TaskSchduler 的 API 文档中的示例说明了如何准确地完成您想要的——它们实现了一个 LimitedConcurrencyLevelTaskScheduler,让您可以设置您想要的工作线程数使用。

API 文档的备注部分中的链接也很有价值。 Samples for Parallel Programming with the .NET Framework 4 project contains a slew of alternative thread schedulers, described in detail here。它们可能会激发您思考安排这些任务的替代方法。

这里唯一的问题是您不能再使用 Task.Run() 快捷方式——您需要通过 TaskFactory instead.

使用 Task.Run() 时,您对作业的控制非常低,一切都是并行的,除非您使用自定义调度程序。

我建议使用 Task Parallel Library (TPL) 而不是这种技术解决方案,它可以被视为处理线程作业的更高层次。

在 TPL 中,您可以选择块类型来处理您的数据,甚至可以在它们之间连接块,因此当一个项目刚刚完成处理时,结果可以在下一个 TPL 块中排队。

您可以使用 ActionBlock<T> :您定义要为每个要处理的项目执行的代码,以及当数据可用于具有 [=13= 的 ActionBlock 时]],它会自动处理...并行处理。但根据您的需要,您可以指定 MaxDegreeOfParallelism=1.

因此,使用这种方法,您无法控制执行代码的核心,但可以确保所有项目都按顺序处理,并且不会同时使用多个核心。

var workerBlock = new ActionBlock<int>(
      // Simulate work by suspending the current thread.
      millisecondsTimeout => Thread.Sleep(millisecondsTimeout),
      // Specify a maximum degree of parallelism.
      new ExecutionDataflowBlockOptions
      {
         MaxDegreeOfParallelism = 1
      });
// Source: https://docs.microsoft.com/fr-fr/dotnet/api/system.threading.tasks.dataflow.actionblock-1?view=netcore-3.1

你也可以看看这篇complete article about TPL,很有意思。