4

我有一个 Backgroundworker,其目的是在后台按顺序运行作业。现在一项工作以多线程方式实现。这意味着,Backgroundworker 将创建多个线程。我使用任务并行库,所以我使用 Task.Factory.StartNew 创建多个任务。

任务运行后,Backgroundworker 等待所有任务完成。

现在我打印 Backgroundworker 的 ManagedThreadID 和所有任务的 ManagedThreadID。我发现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。

4

4 回答 4

5

我会在这里冒险并猜测 TPL 足够聪明,可以重用BackgroundWorker线程。由于工作人员等待所有任务完成在同一线程中运行一个任务可能是一种优化。

通过进一步调查,您所看到的是该Task.Wait方法的预期行为的结果。您可以在并行编程团队博客上的Task.Wait 和“内联”中阅读更多内容。

如果正在等待的任务已经开始执行,则等待必须阻塞。但是,如果它还没有开始执行,Wait 可能能够将目标任务从它排队的调度程序中拉出来,并在当前线程上内联执行它。

于 2010-09-17T12:08:13.267 回答
2

后台工作者从线程池和 TPL 中提取线程。可能发生的情况是后台工作程序启动,它从池中提取一个线程并触发 TPL 线程并立即将线程返回到池中。到 TPL 的第一个任务执行时,TPL 从池中提取了一个线程,并且碰巧它选择了与后台工作人员曾经使用过的线程相同的线程。

当然这只是一个假设,无法验证,因为您没有显示您的代码。

于 2010-09-17T12:06:26.360 回答
2

您偶然发现的当然不是问题,而是一个特性(优化):TPL 正在尽可能多地重用线程。

创建任务时,它不会立即/永久与线程关联。任务是放入队列中的作业,队列由工作线程提供服务。所以可能是 Bgw 任务被挂起并且它的线程返回到池中,或者更直接地它可以由 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();  
于 2010-09-17T12:27:00.507 回答
1

使用 TaskCreationOptions.LongRunning 避免重新循环后台工作程序。

于 2011-02-12T20:31:09.127 回答