2017-05-19 29 views
2

This answer爲什麼同步上下文不能用於等待?

默認的await運營商將捕獲當前的「上下文」,並用它來恢復異步方法。

我想這個代碼在我的控制檯應用程序:

static void Main(string[] args) 
{ 
    Test().Wait(); 
} 

private static async Task Test() 
{ 
    var context = new SynchronizationContext(); 
    SynchronizationContext.SetSynchronizationContext(context); 
    Console.WriteLine("Thread before: " + Thread.CurrentThread.ManagedThreadId); 
    Console.WriteLine(await GetResultAsync()); 
    Console.WriteLine("Thread after: " + Thread.CurrentThread.ManagedThreadId); 
} 

private static async Task<string> GetResultAsync() 
{ 
    return await Task.Factory.StartNew(() => 
    { 
     Console.WriteLine("Thread inside: " + Thread.CurrentThread.ManagedThreadId); 
     return "Hello stackoverflow!"; 
    }); 
} 

...,並得到了這一點:

Thread before: 1 
Thread inside: 3 
Hello stackoverflow! 
Thread after: 3 

爲什麼?如果我想在等待之後使用同一個線程,我應該如何設置同步上下文?

+0

他們在這裏明確地使用術語上下文,因爲並不是所有上下文都綁定到單個線程上。在控制檯應用程序中,沒有任何內建理由比其他人更喜歡一個線程(與使用UI線程的UI框架不同)。 –

回答

4

爲什麼?

new SynchronizationContext()by convention相同一個nullSynchronizationContext。 「無SyncCtx」和「默認SyncCtx」只是將隊列工作到線程池。

SynchronizationContext與特定線程之間不存在1:1的關係。例如:

  • WinForms UI SynchronizationContext會將工作排隊到單個UI線程。 AFAIK,WinForms SynchronizationContext是一個singleton,所以在這種情況下有1:1映射
  • WPF SynchronizationContext會將工作排隊到Dispatcher。上次我檢查時,WPF將爲每個頂層窗口創建一個新的實例,即使它們全都使用相同的線程。所以有一個N:1映射。
  • 線程池(默認/ nullSynchronizationContext可以將工作排隊到任何線程池線程。如果您未創建默認的SynchronizationContext實例,則存在1:N映射。

而且我還應該如何設置同步上下文如果我想在等待後使用同一個線程?

您需要使用自定義SynchronizationContext。我建議使用我的AsyncContext or AsyncContextThread types,因爲這不是直接寫的代碼。

+0

實際上,在WPF'SynchronizationContext.Current'中'await'之後永遠不會有相同的任務完成異步。這是因爲Dispatcher對每個DispatcherSynchronizationContext實例執行每個回調。 IMO的「ConfigureAwait」參數的'continueOnCapturedContext'參數存在爭議。 – Noseratio

+0

奇怪!那幾年以來,這種行爲已經發生了變化。 –

1

我不是這方面的專家,我只是讀過一些教程。

await後面的代碼將作爲一個任務繼續與捕獲的同步上下文一起運行。您提供了new SynchronizationContext(),它使用ThreadPool來執行該代碼。

的源代碼:link

Send方法SynchronizationContext類:

public virtual void Post(SendOrPostCallback d, Object state) 
{ 
    ThreadPool.QueueUserWorkItem(new WaitCallback(d), state); 
} 

所以你看,延續不會在main線程上運行。它使用ThreadPool

GetResultAsync()完成時,thread 3可以自由使用,並立即在SynchronizationContext中被ThreadPool.QueueUserWorkItem重複使用。

因此,您需要爲控制檯應用程序創建自己的同步上下文。

沒有閱讀,但也許這link將幫助。

相關問題