天天看點

C# -- Async 和 Await 解惑

1. Async 官方文檔上的解釋

關于C#

我覺得最下面關于 await 在文檔上的了解比較好,^^

使用 async 修飾符可将方法、lambda 表達式或匿名方法指定為異步。 如果用在方法或表達式使用此修飾符,則其稱為異步方法

public async Task<int> ExampleMethodAsync()  
{  
    // . . . .        

an async method uses the await keyword to do pentially long-running work without blocking the caller’s thread

長時間運作的工作而不會阻塞調用方的線程

string contents = await      

Async modify 方法将同步運作,直至到達其第一個 await 表達式,此時會将方法挂起,直到等待的任務完成。 同時,如下節示例中所示,控件将傳回到方法的調用方

如果 async 關鍵字修改的方法不包含 await 表達式或語句,則該方法将同步執行。 編譯器警告将通知你不包含 await 的任何異步方法,因為該情況可能表示存在錯誤

async 關鍵字是上下文關鍵字,原因在于隻有當它修飾方法、lambda 表達式或匿名方法時,它才是關鍵字。 在所有其他上下文中,都會将其解釋為辨別符

1.1 Example (想運作看最上面連結)

The following example shows the structure and flow of control between

  • an async event handler
  • StartButton_Click
  • and an async method
  • ExampleMethodAsync
private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    // ExampleMethodAsync returns a Task<int>, which means that the method eventually produces an int result. 
    //However, ExampleMethodAsync returns the Task<int> value as soon as it reaches an await.  
    ResultsTextBox.Text += "\n";
    try
    {
        int length = await ExampleMethodAsync();
        // Note that you could put "await ExampleMethodAsync()" in the next line where  
        // "length" is, but due to when '+=' fetches the value of ResultsTextBox, you  
        // would not see the global side effect of ExampleMethodAsync setting the text.  
        ResultsTextBox.Text += String.Format("Length: {0}\n", length);
    }
    catch (Exception)
    {
        // Process the exception if one occurs.  
    }
}

public async Task<int> ExampleMethodAsync()
{
    var httpClient = new HttpClient();
    int exampleInt = (await httpClient.GetStringAsync("http://msdn.microsoft.com")).Length;
    ResultsTextBox.Text += "Preparing to finish ExampleMethodAsync.\n";
    // After the following return statement, any method that's awaiting  
    // ExampleMethodAsync (in this case, StartButton_Click) can get the   
    // integer result.  
    return exampleInt;
}
// Output:  
// Preparing to finish ExampleMethodAsync.  
// Length: 53292        

1.2. 傳回類型

異步方法的傳回類型可以為 Task、Task 或 void。 方法不能聲明任何 ref 或 out 參數,但是可以調用具有這類參數的方法。

如果異步方法的傳回語句指定一個 TResult 類型的操作數,則你應指定 Task 作為方法的傳回類型。 如果當方法完成時未傳回有意義的值,則應使用 Task。 即,對方法的調用将傳回一個 Task,但是當 Task 完成時,任何等待 Task 的所有 await 表達式的計算結果都為 void。

你應主要使用 void 傳回類型來定義事件處理程式,這些處理程式需要此傳回類型。 void 傳回異步方法的調用方不能等待,并且無法捕獲該方法引發的異常

2. Await

await 運算符在異步方法應用于任務,以挂起執行方法,直到所等待的任務完成。挂起在下面解釋, 任務表示正在進行的工作

使用 await 的異步方法必須被async 關鍵字修飾。 使用 async 修飾符定義并且通常包含一個或多個 await 表達式的這類方法稱為異步方法

應用 await 運算符的任務通常是實作基于任務的異步模式的方法調用的傳回值。 示例包括 Task 或 Task 類型的值

2.1 await 傳回類型

如果 await 應用于傳回 ​

​Task<TResult>​

// Keyword await used with a method that returns a Task<TResult>.  
TResult result = await AsyncMethodThatReturnsTaskTResult();  

// Keyword await used with a method that returns a Task.        

await 表達式不阻止正在執行它的線程(簡單了解為主線程)。 而是使編譯器将剩下的異步方法注冊為等待任務的延續任務。 控制權随後會傳回給異步方法的調用方。 任務完成時(誰執行任務?挂起的又是誰?主線程;執行完任務的主線程(此時異步任務沒結束,是以要刮起等待結果)),它會調用其延續任務,異步方法的執行會在暫停的位置處恢複。

await 表達式隻能在由 async 修飾符标記的立即封閉方法體( the body of an immediately enclosing method)、lambda 表達式或異步方法中出現。 術語“await”在該上下文中僅用作關鍵字。 在其他位置,它會解釋為辨別符。 在方法、lambda 表達式或匿名方法中,await 表達式不能在同步函數體、查詢表達式、lock 語句塊或不安全上下文中出現

C# -- Async 和 Await 解惑

關于文檔上關于 await 讓我困惑很久的一段話

An await expression does not block the thread on which it is executing. Instead, it causes the compiler to sign up the rest of the async method as a continuation on the awaited task

await 表達式不阻止正在執行它的線程(簡單了解為主線程)。 而是使編譯器将剩下的異步方法(主線程結束後還在執行的異步線程)注冊為等待任務的延續任務。 控制權随後會傳回給異步方法的調用方。 任務完成時,它會調用其延續任務,異步方法的執行會在暫停的位置處恢複

3. 官方圖解

4. 放碼過來

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

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

            DoIndependentWork();

            string urlContents = await getStringTask;

            Console.WriteLine("---------------"+urlContents.Length);
            return urlContents.Length;
        }

        void DoIndependentWork()
        {
            Console.WriteLine("----------");
        }
        static void Main(string[] args)
        {
            Program pr = new Program();
            Task<int> my = pr.AccessTheWebAsync();
            Console.WriteLine(my.Result);
            Console.ReadLine();
        }
    }