2014-04-14 46 views
11

這裏有一個簡單的WinForms應用程序:瞭解TaskScheduler.Current的行爲

using System; 
using System.Diagnostics; 
using System.Threading; 
using System.Threading.Tasks; 
using System.Windows.Forms; 

namespace WindowsFormsApplication 
{ 
    public partial class Form1 : Form 
    { 
     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private async void button1_Click(object sender, EventArgs e) 
     { 
      var ts = TaskScheduler.FromCurrentSynchronizationContext(); 
      await Task.Factory.StartNew(async() => 
      { 
       Debug.WriteLine(new 
       { 
        where = "1) before await", 
        currentTs = TaskScheduler.Current, 
        thread = Thread.CurrentThread.ManagedThreadId, 
        context = SynchronizationContext.Current 
       }); 

       await Task.Yield(); // or await Task.Delay(1) 

       Debug.WriteLine(new 
       { 
        where = "2) after await", 
        currentTs = TaskScheduler.Current, 
        thread = Thread.CurrentThread.ManagedThreadId, 
        context = SynchronizationContext.Current 
       }); 

      }, CancellationToken.None, TaskCreationOptions.None, scheduler: ts).Unwrap(); 
     } 
    } 
} 

調試輸出中(該按鈕被點擊時):

 
{ where = 1) before await, currentTs = System.Threading.Tasks.SynchronizationContextTaskScheduler, thread = 9, context = System.Windows.Forms.WindowsFormsSynchronizationContext } 
{ where = 2) after await, currentTs = System.Threading.Tasks.ThreadPoolTaskScheduler, thread = 9, context = System.Windows.Forms.WindowsFormsSynchronizationContext } 

問題:爲什麼TaskScheduler.Current改變從SynchronizationContextTaskSchedulerThreadPoolTaskScheduler之後await在這裏?

這實質上表現出行爲TaskCreationOptions.HideSchedulerawait延續,這在我看來是意想不到的和不受歡迎的。

AspNetSynchronizationContext and await continuations in ASP.NET

這個問題已經被我的另一個問題引發的。

回答

12

如果沒有實際的任務正在執行,那麼TaskScheduler.CurrentTaskScheduler.Default相同。換句話說,ThreadPoolTaskScheduler實際上既作爲線程池任務調度程序的含義,表示「沒有當前任務調度程序」。

async代理的第一部分明確使用SynchronizationContextTaskScheduler進行調度,並在UI線程上運行,同時具有任務調度程序和同步上下文。任務調度程序將該委託轉發到同步上下文。

await捕獲其上下文時,捕獲同步上下文(而不是任務調度程序),並使用該syncctx來恢復。所以,方法的繼續被髮布到那個在UI線程上執行它的syncctx。

當繼續運行在UI線程上時,它的行爲與事件處理程序非常相似;委託直接執行,不包含在任務中。如果您在button1_Click開頭檢查TaskScheduler.Current,您會發現它也是ThreadPoolTaskScheduler。作爲實現細節,我建議你將這種行爲(直接執行代理,不包含在任務中)視爲對待。

+0

這是我以爲它的工作原理,但不想在沒有更多信息的情況下發表評論。 +1。 –

+0

我明白了,顯然這是'TaskAwaiter'的工作原理。國際海事組織(IMO),他們爲「流動」TaskScheduler.Current而設計的這一設計頗爲令人困惑:通常情況下,這並不是你所期望的。 – Noseratio

+4

同意; 'TaskScheduler.Current'在設計時考慮了動態並行性,以便子任務從父任務繼承調度器。這種默認行爲對異步任務來說很混亂,這就是爲什麼我堅持在每個'StartNew'和'ContinueWith'中明確指定調度器的原因。 –