【发布时间】:2015-05-19 06:08:31
【问题描述】:
考虑下面的代码
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(缓存)的数据。
我尝试过的事情
- 将 TaskScheduler.FromCurrentSynchronizationContext() 添加到 StartNew 调用。这会导致 StartNew lambda 中的代码永远不会执行(即使它执行了,我也不希望它在主线程上运行)。
- 从 ContinueWith 中删除 TaskScheduler.FromCurrentSynchronizationContext()(并保留 cancelToken 或 TaskContinuationOptions 但不能同时保留,因为没有重载仅支持这两个参数)。这似乎有效(代码执行),但我担心副作用,因为我不确定为什么它有效。
【问题讨论】:
-
听起来你阻塞了 UI 线程,所以无法向它发布延续。
-
您能否简要介绍一下您正在开发的应用程序(Web、Windows、WPF 等)?以及您要在第 1 块中实现的功能是什么?
-
@vendettamit 添加了背景和代码意图。希望这会有所帮助。
-
UI 挂起了吗?在手头或您希望任务运行期间暂停调试器。堆栈上有什么?
-
“在主线程(我从中调用此异步代码)上,我正在等待从代码块 #3 填充结果,然后再继续。” -- 是的,Servy 是对的。您希望块 #2 在 UI 线程上运行,但您在块 #3 完成之前阻止 UI 线程,并且块 #3 仅在块 #2 之后运行。那永远行不通。为什么需要阻塞 UI 线程?如果您还不想运行其余的初始化,您能否不简单地将其余的初始化移动到块 #3 之后运行的延续?
标签: c# .net asynchronous .net-4.5