简体   繁体   English

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

[英]Why doesn't my Task.ContinueWith execute in .NET 4.5?

Consider the following code 请考虑以下代码

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
  );

In .NET 4.0, this code executes as I expect: the first block runs in a background thread, then the second block runs, and finally the third block runs. 在.NET 4.0中,此代码按预期执行:第一个块在后台线程中运行,然后第二个块运行,最后第三个块运行。

In .NET 4.5, however, the second block never runs, no matter what happens with the first block (success, fault, or cancellation). 但是,在.NET 4.5中,无论第一个块发生了什么(成功,故障或取消),第二个块都不会运行。 And the third block doesn't run either, waiting for the non-starting second block. 并且第三个块也不运行,等待非启动的第二个块。

Application background 申请背景

This code is in a WPF application. 此代码位于WPF应用程序中。 It's running during the initialization of the application, loading some data necessary for the app to start. 它在应用程序初始化期间运行,加载应用程序启动所需的一些数据。 On the main thread (where I call this async code from), I am waiting for the result to be populated from code block #3 before continuing. 在主线程(我称之为异步代码)中,我正在等待从代码块#3填充结果,然后再继续。 If the remote data call times out, initialization will continue with the data from block #1 (cached). 如果远程数据调用超时,则将继续初始化来自块#1(缓存)的数据。

Things I've tried 我尝试过的事情

  1. Add the TaskScheduler.FromCurrentSynchronizationContext() to the StartNew call. 将TaskScheduler.FromCurrentSynchronizationContext()添加到StartNew调用。 This causes the code in the StartNew lambda never to execute (and even if it did, I wouldn't want it to run on the main thread). 这导致Star​​tNew lambda中的代码永远不会执行(即使它确实如此,我也不希望它在主线程上运行)。
  2. Remove the TaskScheduler.FromCurrentSynchronizationContext() from the ContinueWith (and keep either the cancellationToken or the TaskContinuationOptions but not both because no overload supports only those two parameters). 从ContinueWith中删除TaskScheduler.FromCurrentSynchronizationContext()(并保留cancellationToken或TaskContinuationOptions,但不要同时保留两者,因为没有重载只支持这两个参数)。 This appears to work (the code executes), but I'm concerned about side effects because I'm not sure why it works. 这似乎工作(代码执行),但我担心副作用,因为我不知道为什么它的工作原理。

Here we have problem with design of ContinueWith and TaskScheduler's properties Current and Default in these two versions of .Net 在这两个版本的.Net中,我们遇到了ContinueWith和TaskScheduler属性Current和Default的设计问题

In .Net 4.0 the TaskScheduler's Current and Default both has same value ie ThreadPoolTaskScheduler , which is ThreadPool's context scheduler and it's not the one that updates the UI ie SynchronizationContextTaskScheduler which is why your code is running fine in .Net 4.0. 在.Net 4.0中,TaskScheduler的Current和Default都具有相同的值,即ThreadPoolTask​​Scheduler ,它是ThreadPool的上下文调度程序,它不是更新UI的那个,即SynchronizationContextTaskScheduler ,这就是你的代码在.Net 4.0中正常运行的原因。

In .Net 4.5 things have changed. 在.Net 4.5中,事情发生了变化。 So when you say TaskScheduler.Current and TaskScheduler.Default then you'll get two different schedulers (in your case when WPF) 所以,当你说TaskScheduler.Current和TaskScheduler.Default然后你会得到两个不同的调度程序(在你的情况下WPF时)

Current is = SynchronizationContextTaskScheduler Current is = SynchronizationContextTaskScheduler

Default is = ThreadPoolTaskScheduler 默认是= ThreadPoolTask​​Scheduler

Now coming back your problem, When you use ContinueWith option it has hard coded value for scheduler as TaskScheduler.Current. 现在回到你的问题,当你使用ContinueWith选项时,它具有调度程序的硬编码值作为TaskScheduler.Current。 Specifically in WPF and Asp.net SynchronizationContextTaskScheduler means it's the UI thread synchronization context and once it's blocked nothing else will execute associated with it until the current executing thread is finished which running with in the UI Thread context. 具体来说,在WPF和Asp.net中,SynchronizationContextTaskScheduler意味着它是UI线程同步上下文,一旦被阻止,在执行当前执行线程并在UI线程上下文中运行之前,其他任何内容都不会执行。

Suggestions(.Net 4.5): Try passing TaskScheduler.Default(NON UI Scheduler) in ContiueWith or avoid using ContinueWith and rather join the tasks in queuing manner. 建议(.Net 4.5): 尝试在ContiueWith中传递TaskScheduler.Default(NON UI Scheduler)或避免使用ContinueWith,而是以排队方式加入任务。

I hope this will give your clear picture why the behaviour is changing. 我希望这能清楚地说明行为正在发生变化的原因。 For more details see this discussion: Why is TaskScheduler.Current the default TaskScheduler? 有关更多详细信息,请参阅此讨论: 为什么TaskScheduler.Current是默认的TaskScheduler?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM