簡體   English   中英

.NET異步\等待基礎

[英].NET async\await fundamentals

在使用.NET 4.5 async \\ await框架后,我有一個問題。

我們來看看這個程序( msdn示例 ):

    async Task<int> AccessTheWebAsync()
    {
        HttpClient client = new HttpClient();

        Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

        DoIndependentWork();

        string urlContents = await getStringTask;

        return urlContents.Length;
    }


    void DoIndependentWork()
    {
        resultsTextBox.Text += "Working . . . . . . .\r\n";
    }

該程序將按以下順序運行:

  1. 新的HttpClient。
  2. GetStringAsync同步調用。
  3. 在某些時候,GetStringAsync調用await,控件返回AccessTheWebAsync。
  4. 調用了DoIndependentWork。
  5. 程序等待返回的字符串(如果操作沒有完成則阻塞)。
  6. 返回urlContent的長度。

我花了一些時間來理解的一件事是GetStringAsync方法盡管它的名稱同步運行(名稱約定實際上是誤導性的)。

在我們異步運行該方法的過程中,我們需要顯式地使用Task.RunTask.Factory.StartNew

但真正的問題是,如果我們有獨立的工作,為什么不馬上做,而不是等待等待從GetStringAsync調用? (換句話說,為什么異步方法不能按定義異步運行?)

編輯:我將改寫第二和第三個操作:

(2)GetStringAsync同步啟動。

(3)在某些時候,GetStringAsync調用await和線程分叉,控件返回AccessTheWebAsync。

我花了一些時間來理解的一件事是GetStringAsync方法盡管它的名稱同步運行(名稱約定實際上是誤導性的)。

那是不對的。 GetStringAsync返回Task<string> 它將立即返回,這意味着DoIndependentWork將(可能)在下載完成之前運行。 await運算符將異步等待,直到GetStringAsync返回的Task<T>完成。

但真正的問題是,如果我們有獨立的工作,為什么不馬上做,而不是等待等待從GetStringAsync調用? (換句話說,為什么異步方法不能按定義異步運行?)

返回方法后,異步方法的實際“工作”正在運行。 返回的Task將(通常)處於未完成的狀態,這意味着該方法仍然是異步運行的。 您可以檢查Task.IsCompleted以進行驗證。

試試這個:

Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
Debug.WriteLine("Completed? {0}", getStringTask.IsCompleted); // Will likely print false
DoIndependentWork();
string urlContents = await getStringTask;
Debug.WriteLine("Completed now? {0}", getStringTask.IsCompleted); // Will always print true

每個async方法在每個await分成段。 每個段將成為編譯器生成的狀態機上的狀態。

每個await指令適用於一個awaitable對於這一個Task是最常見的情況。

每個狀態/段將同步執行,直到檢查接收到的等待的位置為止已完成。

如果等待完成,它將繼續執行下一個狀態。

如果等待未完成並且沒有當前的SynchronizationContext ,則執行將被阻止,直到等待完成為止,此時將開始執行下一狀態。

如果存在當前的SynchronizationContext ,則執行將返回給調用者,並且當等待完成時,將繼續到下一狀態將被發布到捕獲的SynchronizationContext

程序等待返回的字符串(如果操作沒有完成則阻塞)。

await不會阻止await getStringTask 它將在編譯器生成的狀態機對象中保存AccessTheWebAsync方法的內部狀態(局部變量和執行點所在的位置),並返回調用AccessTheWebAsync的外部方法。 狀態將被恢復,並且當getStringTask任務完成后,將通過編譯器生成的延續回調異步恢復執行。 如果您熟悉C#迭代器和yield關鍵字,則await狀態機控制流與它非常相似,盡管迭代器是同步執行的。

await之后執行將如何恢復取決於啟動await的線程的同步上下文 如果它是一個抽取Windows消息的UI線程,則可能會在線程消息循環的下一次迭代(通常在Application.Run )調用延續回調。 如果它是非UI線程(例如,控制台應用程序),則繼續可能在不同的線程上發生。 因此,雖然您的方法的控制流仍然是邏輯線性的,但從技術上講它不是(如果您只是執行getStringTask.Wait()而不是await getStringTask )。

程序等待返回的字符串(如果操作沒有完成則阻塞)。

不,它沒有。 await 等待 ,但它不會阻止任何線程。 這就是重點。

我花了一些時間來理解的一件事是GetStringAsync方法盡管它的名稱同步運行(名稱約定實際上是誤導性的)。

那是錯的。 大多數方法都是異步運行的。 它很可能在開始時也有一個小的同步部分,但這應該可以忽略不計。

在我們異步運行該方法的過程中,我們需要顯式地使用Task.RunTask.Factory.StartNew

不,該方法已經異步運行。 如果你想在另一個線程上運行小同步部分,那么你可以使用Task.Run() ,但這幾乎沒有任何意義。 此外,不要使用StartNew() ,它不適用於異步方法。

換句話說,為什么異步方法不能按定義異步運行?

關於await的重要一點是它在它離開的相同環境中恢復。

這在GUI應用程序中非常有用,您經常需要修改UI,然后執行異步操作,然后再次修改UI。 如果async自動意味着整個方法在ThreadPool線程上執行,那么這是不可能的。

此外,這樣做更有效率,因為您不必為需要很少時間的事情切換到另一個線程。

暫無
暫無

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

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