考虑以下代码
Task<T>.Factory.StartNew(() =>
{
// block #1: load some data from local file cache
}
)
.ContinueWith(task =>
{
// block #2: handle success or failure of load-from-cache operation and surface to application
},
cancellationToken,
TaskContinuationOptions.NotOnCanceled,
TaskScheduler.FromCurrentSynchronizationContext()
)
.ContinueWith(task =>
{
// block #3: load data from remote data source
},
TaskContinuationOptions.NotOnCanceled
);
在.NET 4.0中,此代码按预期执行:第一个块在后台线程中运行,然后第二个块运行,最后第三个块运行。
然而,在.NET 4.5中,无论第一个块发生了什么(成功,故障或取消),第二个块都不会运行。并且第三个块也没有运行,等待非启动的第二个块。
此代码位于WPF应用程序中。它在应用程序初始化期间运行,加载应用程序启动所需的一些数据。在主线程(我称之为异步代码)中,我正在等待从代码块#3填充结果,然后再继续。如果远程数据调用超时,则初始化将继续使用来自块#1(缓存)的数据。
答案 0 :(得分:2)
在这两个版本的.Net中,我们遇到了ContinueWith和TaskScheduler属性Current和Default的设计问题
在.Net 4.0中,TaskScheduler的Current和Default都具有相同的值,即 ThreadPoolTaskScheduler ,这是ThreadPool的上下文调度程序,它不是更新UI的那个,即 SynchronizationContextTaskScheduler 这就是你的代码在.Net 4.0中正常运行的原因。
在.Net 4.5中,事情发生了变化。所以当你说TaskScheduler.Current和TaskScheduler.Default然后你会得到两个不同的调度程序(在你的情况下是WPF)
当前是= SynchronizationContextTaskScheduler
默认是= ThreadPoolTaskScheduler
现在回到你的问题,当你使用ContinueWith选项时,它具有调度程序的硬编码值作为TaskScheduler.Current。具体来说,在WPF和Asp.net中,SynchronizationContextTaskScheduler意味着它是UI线程同步上下文,一旦被阻止,其他任何其他内容都不会执行,直到当前正在执行的线程完成,并在UI线程上下文中运行。
建议(.Net 4.5): 尝试在ContiueWith中传递TaskScheduler.Default(NON UI Scheduler)或避免使用ContinueWith,而是以排队方式加入任务。 < / p>
我希望这能让你清楚地了解行为正在发生变化的原因。有关详细信息,请参阅此讨论:Why is TaskScheduler.Current the default TaskScheduler?