c# - 为什么我的 Task.ContinueWith 不在 .NET 4.5 中执行?

标签 c# .net asynchronous .net-4.5

考虑以下代码

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 中,此代码按我的预期执行:第一个 block 在后台线程中运行,然后第二个 block 运行,最后第三个 block 运行。

但是,在 .NET 4.5 中,无论第一个 block 发生什么情况(成功、错误或取消),第二个 block 都不会运行。第三 block 也不运行,等待非启动第二 block 。

应用背景

此代码位于 WPF 应用程序中。它在应用程序初始化期间运行,加载应用程序启动所需的一些数据。在主线程(我从中调用此异步代码)上,我正在等待从代码块 #3 填充结果,然后再继续。如果远程数据调用超时,初始化将继续使用 block #1(缓存)中的数据。

我尝试过的事情

  1. 将 TaskScheduler.FromCurrentSynchronizationContext() 添加到 StartNew 调用中。这会导致 StartNew lambda 中的代码永远不会执行(即使执行了,我也不希望它在主线程上运行)。
  2. 从 ContinueWith 中删除 TaskScheduler.FromCurrentSynchronizationContext()(并保留 cancellationToken 或 TaskContinuationOptions 但不能同时保留两者,因为没有重载仅支持这两个参数)。这似乎有效(代码执行),但我担心副作用,因为我不确定为什么它有效。

最佳答案

在这两个版本的 .Net 中,ContinueWith 和 TaskScheduler 的属性 Current 和 Default 的设计存在问题

在 .Net 4.0 中,TaskScheduler 的 Current 和 Default 都具有相同的值,即 ThreadPoolTask​​Scheduler,它是 ThreadPool 的上下文调度程序,而不是更新 UI 的那个,即 SynchronizationContextTaskScheduler这就是您的代码在 .Net 4.0 中运行良好的原因。

在 .Net 4.5 中,情况发生了变化。因此,当您说 TaskScheduler.Current 和 TaskScheduler.Default 时,您将获得两个不同的调度程序(在您使用 WPF 的情况下)

当前为 = SynchronizationContextTaskScheduler

默认为 = ThreadPoolTask​​Scheduler

现在回到您的问题,当您使用 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/

相关文章:

c# - 模拟和 BackgroundWorker

c# - 在 .aspx 页面中获取设置值?

javascript - iOS中的同步与异步队列

javascript - 在 React.JS 中将 props 的值从函数传递给 render()

c# - 跨部分类的 "textual order"是否正式定义?

c# - 在 ASP.NET 中使用带有友好 URL 的 Response.Redirect

c# - 重写 == 并让实例比较等于 null 是一件坏事吗?

Java 客户端通过 SSL 使用 .NET WCF(不支持 WS-Policy)

c# - 在运行时将图像添加到 Crystal Report

javascript - 如何在另一个函数中使用 $.getJSON 返回的变量