簡體   English   中英

await在異步操作后沒有恢復上下文?

[英]await does not resume context after async operation?

我已經從Noseratio中讀到了這個問題該問題顯示了一個行為,即在等待完成其操作后, TaskScheduler.Current 不同

答案指出:

如果沒有執行任何實際任務 ,則TaskScheduler.CurrentTaskScheduler.Default相同

這是真的。 我已在這里看到它:

  • TaskScheduler.Default
    • 返回ThreadPoolTaskScheduler一個實例
  • TaskScheduler.Current
    • 如果從執行任務中調用,則返回當前正在執行的任務的TaskScheduler
    • 如果從任何其他地方調用將返回TaskScheduler.Default

但轉念一想,如果是這樣,讓我們創建一個實際的Task (不只是Task.Yield()並對其進行測試:

async void button1_Click_1(object sender, EventArgs e)
{
    var ts = TaskScheduler.FromCurrentSynchronizationContext();
    await Task.Factory.StartNew(async () =>
    {
        MessageBox.Show((TaskScheduler.Current == ts).ToString()); //True

           await new WebClient().DownloadStringTaskAsync("http://www.google.com");

        MessageBox.Show((TaskScheduler.Current == ts).ToString());//False

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

第一個Messagebox是“True”,第二個是“False”

題:

如您所見,我確實創建了一個實際任務。

我可以理解為什么第一個MessageBox產生True 多數民眾贊成:

如果從執行任務中調用,則返回當前正在執行的任務的TaskScheduler

並且該任務確實具有ts ,即發送的TaskScheduler.FromCurrentSynchronizationContext()

為什么第二個 MessageBox中沒有保留上下文? 對我來說,斯蒂芬的回答並不清楚。

附加信息 :

如果我寫(第二個消息框):

MessageBox.Show((TaskScheduler.Current == TaskScheduler.Default).ToString());

它確實產生了true 但為什么 ?

混淆的原因是:

  1. UI沒有“特殊” TaskScheduler 在UI線程上運行的代碼的默認情況是TaskScheduler.Current存儲ThreadPoolTaskSchedulerSynchronizationContext.Current存儲WindowsFormsSynchronizationContext (或其他UI應用程序中的相關文件)
  2. TaskScheduler.Current中的ThreadPoolTaskScheduler並不一定意味着它是用於運行當前代碼段的TaskScheduler 它還意味着TaskSchdeuler.Current == TaskScheduler.Default ,因此“沒有使用TaskScheduler
  3. TaskScheduler.FromCurrentSynchronizationContext()不返回“acutal” TaskScheduler 它返回一個“代理”,可以將任務直接發布到捕獲的SynchronizationContext

因此,如果您在開始任務之前(或在任何其他地方)運行測試,您將獲得與等待之后相同的結果

MessageBox.Show(TaskScheduler.Current == TaskScheduler.FromCurrentSynchronizationContext()); // False

因為TaskScheduler.CurrentThreadPoolTaskSchedulerTaskScheduler.FromCurrentSynchronizationContext()返回SynchronizationContextTaskScheduler

這是你的例子的流程:

  • 您創建一個新的SynchronizationContextTaskScheduler從UI的SynchronizationContext (即WindowsFormsSynchronizationContext )。
  • TaskScheduler上使用Task.Factory.StartNew計划您創建的任務。 由於它只是一個“代理”,它將委托發布到WindowsFormsSynchronizationContext ,后者在UI線程上調用它。
  • 該方法的同步部分(第一次等待之前的部分)在UI線程上執行,同時與SynchronizationContextTaskScheduler相關聯。
  • 該方法到達await並在捕獲WindowsFormsSynchronizationContext時被“掛起”。
  • 在等待之后恢復繼續時,它將被發布到該WindowsFormsSynchronizationContext而不是SynchronizationContextTaskScheduler因為SynchronizationContext具有優先權( 這可以在Task.SetContinuationForAwait看到 )。 然后它在UI線程上定期運行,沒有任何“特殊” TaskScheduler因此TaskScheduler.Current == TaskScheduler.Default

因此,創建的任務在使用SynchronizationContext的代理TaskScheduler上運行,但是在await發布到SynchronizationContext而不是TaskScheduler之后的延續。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM