考虑以下代码
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(缓存)中的数据。
我尝试过的事情
最佳答案
在这里,我们在这两个版本的 .Net 中设计 ContinueWith 和 TaskScheduler 的属性 Current 和 Default 时遇到问题
在 .Net 4.0 中,TaskScheduler 的 Current 和 Default 都具有相同的值,即 ThreadPoolTaskScheduler ,它是 ThreadPool 的上下文调度程序,它不是更新 UI 的程序,即 SynchronizationContextTaskScheduler ojit.rstrong 为什么在你的代码中运行良好。
在 .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 而是以队列方式加入任务。
我希望这能让您清楚地了解行为发生变化的原因。有关更多详细信息,请参阅此讨论:Why is TaskScheduler.Current the default TaskScheduler?
关于c# - 为什么我的 Task.ContinueWith 不在 .NET 4.5 中执行?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/30312632/