【发布时间】:2013-11-19 23:40:34
【问题描述】:
我有一个处理用户创建的任务的 Windows 服务。该服务在具有 4 个核心的服务器上运行。这些任务主要涉及繁重的数据库工作(例如生成报告)。服务器还运行了一些其他服务,所以我不想启动太多线程(假设最多 4 个)。
如果我使用 BlockingCollection<MyCustomTask>,创建 4 个 Thread 对象并使用这些对象从 BlockingCollection<MyCustomTask> 中使用是更好的主意,还是应该使用 Parallel.Foreach 来完成此操作?
我正在查看包含使用前者的 StaTaskScheduler 的 ParallelExtensionsExtras,就像这样(为了清晰起见,稍微修改了代码):
var threads = Enumerable.Range(0, numberOfThreads).Select(i =>
{
var thread = new Thread(() =>
{
// Continually get the next task and try to execute it.
// This will continue until the scheduler is disposed and no more tasks remain.
foreach (var t in _tasks.GetConsumingEnumerable())
{
TryExecuteTask(t);
}
});
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
return thread;
}).ToList();
// Start all of the threads
threads.ForEach(t => t.Start());
但是,在同一个 ParallelExtensionsExtras 中还有一个 BlockingCollectionPartitioner 可以在 BlockingCollection<Task> 上使用 Parallel.Foreach,如下所示:
var blockingCollection = new BlockingCollection<MyCustomTask>();
Parallel.ForEach(blockingCollection.GetConsumingEnumerable(), task =>
{
task.DoSomething();
});
据我了解,后者利用了ThreadPool。在这种情况下使用 Parallel.ForEach 有什么好处吗?
【问题讨论】:
-
你写的没有多大意义。 Tasks 和 Parallel.ForEach 基于线程。因此,您运行线程来运行任务(例如运行更多线程)。此解决方案不会帮助您提高性能。
-
Task 是一个定制的类(即不是 System.Threading.Tasks.Task)。我已编辑问题以使其更清楚。
-
如果 TPL 确定一组任务通过并行运行将获得很少或没有任何好处,它将选择按顺序执行它们而不是并行执行。是的,它利用 ThreadPool 并管理并发您的代表
-
要记住的一点是,如果阻塞集合花费大量时间为空,
Parallel.ForEach可能会启动过多的线程池线程(这在并行编程模式中进行了讨论,第 88 页)。您说您只希望 4 个线程处理您的“任务”,因此请务必通过ParallelOptions.MaxDegreeOfParallelism限制并行度。
标签: c# task-parallel-library parallel.foreach