我有一个Backgroundworker,其目的是在后台顺序运行作业。现在,一项工作以多线程方式实现。这意味着,Backgroundworker将创建多个线程。我使用任务并行库,所以我使用Task.Factory.StartNew创建多个任务。
运行任务后,Backgroundworker等待所有任务完成。
现在我打印Backgroundworker的ManagedThreadID和所有任务'ManagedThreadIDs。我发现BackgroundWorker的ManagedThreadID始终与第一个任务的ManagedThreadID相同。我认为这不应该发生,所以我无法解释。我认为Backgroundworker的线程必须与它创建的所有任务不同,因此ManagedThreadID必须彼此不同。
任何人都能解释为什么会出现这种情况吗?非常感谢你。
编辑:
代码与此类似:
Backgroundworker.Run(){
// Print Thread.CurrentThread.ManagedThreadID.
var task = Task.Factory.StartNew(action1); // action1, action2 also print ManagedThredID.
taskList.Add(task);
task = Task.Factory.StartNew(action2);
taskList.Add(task);
... // Several other tasks.
foreach(var task in taskList) task.Wait();
}
您会发现一个任务与Backgroundworker具有相同的ManagedThreadID。
答案 0 :(得分:5)
我会在这里继续思考并猜测TPL足够聪明,可以重用BackgroundWorker
线程。由于worker等待所有任务完成,因此在同一个线程中运行一个任务可能是一个优化。
通过进一步调查,您所看到的是Task.Wait
方法的预期行为的结果。您可以在并行编程团队博客上的Task.Wait and "Inlining"阅读更多内容。
如果正在等待的任务有 已经开始执行,等待必须 块。但是,如果还没有开始 执行,等待也许能够拉动 调度程序中的目标任务 它排队并执行它 内联当前线程。
答案 1 :(得分:2)
后台工作程序从线程池和TPL中提取线程。可能发生的是后台工作程序启动,它从池中抽取一个线程并触发TPL线程并立即将线程返回到池中。当TPL的第一个任务被执行时,TPL从池中抽取一个线程,它发现它选择了与后台工作者曾经使用过的线程相同的线程。
当然,这只是一个无法验证的假设,因为你没有展示你的代码。
答案 2 :(得分:2)
你偶然发现的当然不是问题,而是一个功能(优化):TPL正在尽可能多地重用线程。
创建任务时,它不会立即/永久地与线程关联。任务是放在队列中的作业,队列由工作线程提供服务。因此可能是Bgw Task被挂起并且它的线程返回到池中,或者更直接地可以通过Wait()来完成:
// thread A
var t1 = Task.Startnew(...);
var t2 = Task.Startnew(...);
t1.Wait(); // Thread A is idle/available so Wait can execute t1
t2.Wait();
答案 3 :(得分:1)
使用TaskCreationOptions.LongRunning避免重新循环后台工作程序。