2016-09-02 218 views
2

我有關於異步作業的執行順序的問題。回調異步並等待

我會問我的問題與例子,因爲它更容易可以理解的。

距離https://msdn.microsoft.com/en-us/library/mt674882.aspx一些扭曲的官方例子。

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

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

    // You can do work here that doesn't rely on the string from GetStringAsync. 
    DoWork1(); 

    // The await operator suspends AccessTheWebAsync. 
    string urlContents = await getStringTask; 

    DoWork2(); 

    return urlContents.Length; 
} 

我可以說DoWork2client.GetStringAsync回調?

如果是這樣,DoWork2client.GetStringAsync完成後不立即執行如果DoWork1的運行時間比client.GetStringAsync更長。

我在這兒嗎?

+0

是的,你開始了任務,當'DoWork1()'完成時,你將會等待任務。如果它已經完成,「DoWork2」立即啓動。如果不是的話,當任務完成時'DoWork2'開始。 –

+0

@AlexanderDerck然後它不是我預期的那種「回調」。在我看來,一旦異步函數完成,應立即執行回調。 – derek

+1

只是爲了澄清:即使DoWork1尚未完成,您還是希望DoWork2能夠執行(完成getStringTask)嗎?也許看看[Task.ContinueWith](https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.continuewith.aspx)方法。 –

回答

5

DoWork2不立即執行完成 client.GetStringAsync如果DoWork1運行時間長於 client.GetStringAsync

一旦你打的點await client.GetStrinkAsync以下,DoWork1()已經完成,從你的例子看起來那麼它的執行是同步的。一旦client.GetStringAsync完成,則DoWork2被設置爲執行。

的執行流程爲:

  1. GetStringAsync異步啓動。它會被執行,直到它遇到它的第一個內部await,然後控制回到AccessTheWebAsync
  2. DoWork1()開始。因爲它是同步的,所有人都等待它完成
  3. client.GetStringAsync異步等待完成。 await自然會對執行者的調用者產生控制權。
  4. 一旦client.GetStringAsync完成後,執行將返回AccessTheWebAsyncDoWork2()將同步執行。
  5. urlContents.length將被退回。

通常,第一個await關鍵字之後的所有內容都被設置爲該方法其餘部分的延續。

+0

有沒有辦法讓'DoWork2'立即執行一次客戶端。 GetStringAsync'完成了嗎? – derek

+0

@derek但是,這正是發生的事情。一旦'client.GetStringAsync'完成,'DoWork2'將會執行。 –

+0

說'DoWork1()'需要10秒才能完成,而'client.GetStringAsync'需要5秒。所以當'client.GetStringAsync'完成時,'DoWork1()'仍然在運行,所以我們需要等待'DoWork1()'完成,對嗎?然後5秒鐘後,'DoWork1()'完成。只有在這個時候,'DoWork2()'才能開始工作。糾正我,如果我錯了。 – derek

-1

在:

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

要創建一個運行異步操作,並立即返回給調用者任務,下一DoWork1()執行所同步,一旦做​​暫停方法的延續,直到等待任務完成;一旦完成程序繼續DoWork2()

+1

說'getStringTask'塊與正確「等待」異步方法時發生的情況完全相反。 –

+1

同意:'暫停'或'暫停執行'或將AccessTheWebAsync()的其餘部分指定爲'getStringTask'的延續是更正確的用法。塊不正確(更新) – Ahmad

+1

不,該任務確實立即運行。所有'await'所做的就是接受一個任務(或其他等待),其他代碼片段已經啓動,並且不執行任何操作(如果任務已經完成),或者暫停執行當前方法直到任務完成。但是,只是爲了強調,「等待」不會啓動任何事情。 –

4

Yuval's answer是正確的。

要回答你的後續問題:

有沒有一種方法,使後立即client.GetStringAsync執行DoWork2完了?

可能。這取決於這個代碼的上下文。無法同時在UI線程上運行DoWork1DoWork2(顯然,單個線程一次只能執行一件事)。但是,如果這是從線程池上下文中調用的,那麼當然,可以同時運行它們。

但是,您應該將思路從「回調」轉換爲「操作」。也就是說,不要認爲DoWork2GetStringAsync的「回調」。相反,可以這樣想:我有一個操作GetStringAsync,我有一個操作DoWork2;我需要一個新的操作來完成另一個操作。

一旦你改變你這樣的想法,解決的辦法是寫新的操作方法:

async Task<string> GetStringAndDoWork2Async() 
{ 
    HttpClient client = new HttpClient(); 
    var result = await client.GetStringAsync("http://msdn.microsoft.com"); 
    DoWork2(); 
    return result; 
} 

現在你可以使用你的更高級別的方法,新的操作:

async Task<int> AccessTheWebAsync() 
{ 
    var task = GetStringAndDoWork2Async(); 

    DoWork1(); 

    string urlContents = await task; 

    return urlContents.Length; 
}