TaskScheduler.Current和TaskScheduler.FromCurrentSynchronizationContext()有什么区别?

12

我有一个任务是从数据库获取产品,并使用ContinueWith动作进行一些界面修改,因此我遇到了问题,因为该任务创建了一个新的线程,而UI修改并未在UI线程中执行。

我尝试使用以下修复方法:

var currentScheduler = TaskScheduler.Current;

Task.Factory.StartNew(() =>
{    
    // get products   
}).ContinueWith((x) => handleProductsArrived(x.Result, x.Exception), currentScheduler);

但它根本没起作用。我检查了,ContinueWith 没有在当前调度程序的线程中执行,而是在另一个线程中执行。

我发现了这种方法:

Task.Factory.StartNew(() =>
{
    // get products
}).ContinueWith((x) => handleProductsArrived(x.Result, x.Exception), TaskScheduler.FromCurrentSynchronizationContext());

它起作用了。那么有什么区别呢?为什么我的第一段代码不起作用呢?谢谢!

1个回答

22
TaskScheduler.Current 的文档中可以看出:
当不是在任务内调用时,Current 将返回默认调度程序。
然后从 Task调度程序文档 中可以知道:
Task Parallel Library和PLINQ的默认调度程序使用.NET Framework线程池来排队和执行工作。
因此,如果您在不在任务中且使用TaskScheduler.Current ,则会得到一个使用线程池的调度程序。
如果您调用 TaskScheduler.FromCurrentSynchronizationContext(),则会获取当前 synchronization context的调度程序——在 Windows Forms 或 WPF(从 UI 线程调用时)上下文中,它是一个可安排工作在相关 UI 线程上的上下文。
这就是为什么第一段代码不起作用:它在线程池线程上执行了您的续订。而第二段代码在UI线程上执行了续订。
请注意,如果您可以使用C#5和async/await,所有这些都会更加简单处理。

这在无法像 UI 组件的构造函数中使用 async/await 时非常有用,对吧? - Chin
@Chin:我不会特别地用它来做那件事 - 我通常会有一个静态的异步方法来完成工作,然后调用一个快速构造函数。 - Jon Skeet
你能详细说明一下吗?特别是我在这里有另一个问题: http://stackoverflow.com/questions/31886276/handle-exception-when-using-task-run-in-ui-constructor ,有点相关。 - Chin
构造函数(){ 执行繁重任务并更新(); }private async void 执行繁重任务并更新(){ await Task.Run(()=>{... /在后台线程上计算/ }); /这部分在主线程上运行/ 更新UI(); } - Dominik Weber
@JonSkeet 有什么“更简单”的解决方案吗?我真的很想用它。我的问题是:我有一个基类,将被其他类大量使用。我希望在每个继承类构造函数执行后仅初始化一些属性。因此,我想到了在当前同步上下文中创建任务。我觉得异步等待应该能够解决这个问题,但我无法弄清楚。 - Kobor42
@Kobor42:请在一个新问题上提出详细信息,而不是在一个10年前的问题的评论中,而且这个问题似乎并不真正符合原始问题。 - Jon Skeet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接