国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

.NET基于任務的異步模式(Task-basedAsynchronousPattern,TAP)asyncawait

2019-11-14 16:04:50
字體:
來源:轉載
供稿:網友

本文內容

  • 概述
  • 編寫異步方法
  • 異步程序中的控制流
  • API 異步方法
  • 線程
  • 異步和等待
  • 返回類型和參數
  • 參考資料

下載 Demo

下載 Demo TPL 與 APM 和 EAP 結合(APM 和 EAP 這兩個標準異步方式已經不能適應多核時代,但之前用這兩種方式寫的代碼怎么辦?——把它們改造一下,跟 TPL 結合)

概述


異步對可能起阻止作用的活動(例如,應用程序訪問 Web 時)至關重要。 對 Web 資源的訪問有時很慢或會延遲。 如果此類活動在同步過程中受阻,則整個應用程序必須等待。在異步過程中,應用程序可繼續執行不依賴 Web 資源的其他工作,直至潛在阻止任務完成。

下表是利用異步編程能提高響應能力的典型場景。從 .NET Framework 4.5 和 Windows 運行時中列出的 API 包含支持異步編程的方法。

應用程序區域

包含異步方法的受支持的 API

Web 訪問

HttpClient ,SyndicationClient

使用文件

StorageFile、StreamWriter、StreamReader、xmlReader

使用圖像

MediaCapture、BitmapEncoder、BitmapDecoder

WCF 編程

同步和異步操作

由于所有與用戶界面相關的活動通常共享一個線程,因此,異步對訪問 UI 線程的應用程序來說尤為重要。如果任何進程在同步應用程序中受阻,則所有進程都將受阻。 你的應用程序停止響應,因此,你可能在其等待過程中認為它已經失敗。

使用異步方法時,應用程序將繼續響應 UI。 例如,你可以調整窗口的大小或最小化窗口;如果你不希望等待應用程序結束,則可以將其關閉。

可以使用三種方式來實現 TAP:即手動使用 C# 編譯器,或將編譯器和手動方法結合使用。使用 TAP 模式來實現計算密集型和 I/O 密集型異步操作。

  • 使用編譯器。在 Visual Studio 2012 和 .NET Framework 4.5 中,任何具有 async 關鍵字的方法都被看作是一種異步方法,并且 C# 會執行必要的轉換,以通過 TAP 來異步實現該方法。 異步方法應返回 System.Threading.Tasks.Task 或 System.Threading.Tasks.Task<TResult> 對象。
  • 手動生成 TAP 方法。也可以手動實現 TAP 模式,以更好地控制實現。編譯器依賴從 System.Threading.Tasks 命名空間公開的公共外圍應用和 System.Runtime.CompilerServices 命名空間中支持的類型。 如要自己實現 TAP,你需要創建一個 TaskCompletionSource<TResult> 對象、執行異步操作,并在操作完成時,調用 SetResult、SetException、SetCanceled 方法,或調用這些方法之一的Try版本。 手動實現 TAP 方法時,需在所表示的異步操作完成時完成生成的任務。 例如:
  • public static Task<int> ReadTask(this Stream stream, byte[] buffer, int offset, int count, object state)
    {
        var tcs = new TaskCompletionSource<int>();
        stream.BeginRead(buffer, offset, count, ar =>
        {
            try { tcs.SetResult(stream.EndRead(ar)); }
            catch (Exception exc) { tcs.SetException(exc); }
        }, state);
        return tcs.Task;
    }
  • 混合方法。你可能發現手動實現 TAP 模式、但將實現核心邏輯委托給編譯器的這種方法很有用。 例如,當你想要驗證編譯器生成的異步方法之外的實參時,可能需要使用這種混合方法,以便異常可以轉義到該方法的直接調用方而不是通過 System.Threading.Tasks.Task 對象被公開:
  • public Task<int> MethodAsync(string input)
    {
        if (input == null) throw new ArgumentNullException("input");
        return MethodAsyncInternal(input);
    }
     
    PRivate async Task<int> MethodAsyncInternal(string input)
    {
     
       // code that uses await goes here
     
       return value;
    }

本文主要說明“使用編譯器”方法。

編寫異步方法


C# 中 asyncawait 關鍵字是異步編程的核心。通過這兩個關鍵字就可以輕松創建異步方法,幾乎與創建同步方法一樣。如下所示的 WPF 程序,布局文件上有個按鈕和文本框:

private async void StartButton_Click(object sender, RoutedEventArgs e)
{
    // Call and await separately.
    //Task<int> getLengthTask = accessTheWebAsync();
    //// You can do independent work here.
    //int contentLength = await getLengthTask;
 
    int contentLength = await AccessTheWebAsync();
 
    resultsTextBox.Text +=
        String.Format("/r/nLength of the downloaded string: {0}./r/n", contentLength);
}
 
 
// Three things to note in the signature:
//  - The method has an async modifier. 
//  - The return type is Task or Task<T>. (See "Return Types" section.)
//    Here, it is Task<int> because the return statement returns an integer.
//  - The method name ends in "Async."
async Task<int> AccessTheWebAsync()
{ 
    // You need to add a reference to System.Net.Http to declare client.
    HttpClient client = new HttpClient();
 
    // GetStringAsync returns a Task<string>. That means that when you await the
    // task you'll get a string (urlContents).
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
 
    // You can do work here that doesn't rely on the string from GetStringAsync.
    DoIndependentWork();
 
    // The await Operator suspends AccessTheWebAsync.
    //  - AccessTheWebAsync can't continue until getStringTask is complete.
    //  - Meanwhile, control returns to the caller of AccessTheWebAsync.
    //  - Control resumes here when getStringTask is complete. 
    //  - The await operator then retrieves the string result from getStringTask.
    string urlContents = await getStringTask;
 
    // The return statement specifies an integer result.
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value.
    return urlContents.Length;
}
 
 
void DoIndependentWork()
{
    resultsTextBox.Text += "Working . . . . . . ./r/n";
}

執行結果:

Working . . . . . . .
 
Length of the downloaded string: 41609.

說明:

1,當程序訪問網絡時,無論你如何拖拽、最大化最小化、如何點擊,UI 都不會失去響應;

2,“async Task<int> AccessTheWebAsync()”方法簽名,有三點需要注意:1)有 async 修飾符;2)返回類型是 TaskTask<int>。該方法是 Task<int>,因為它返回的是鏈接內容的大小;3)方法名以 Async 結尾;

3,“string urlContents = await getStringTask;”語句,有四點需要注意:1)AccessTheWebAsync 方法直到 getStringTask 完成才能繼續;2)同時,控制流返回到 AccessTheWebAsync 的調用者;3)getStringTask 完成后,控制流才會恢復;4)之后,await 操作符從 getStringTask 檢索結果。

下面總結讓一個示例成為異步方法的特征:

  • 方法簽名包含一個 async 修飾符。
  • 按照約定,異步方法的名稱以“Async”后綴結尾。
  • 返回類型為下列類型之一:
    • 如果你的方法有 TResult 類型的返回語句,則為 Task<TResult>。
    • 如果你的方法沒有返回語句,則為 Task。
    • 如果你編寫的是異步事件處理程序,則為 Void(Visual Basic 中為 Sub)。
  • 方法通常包含至少一個 await 表達式,該表達式標記一個點,在該點上,直到等待的異步操作完成方法才能繼續。同時,將方法掛起,并且控件返回到方法的調用方。

在異步方法中,可使用提供的關鍵字和類型來指示需要完成的操作,且編譯器會完成其余操作,其中包括持續跟蹤控件以掛起方法返回等待點時發生的情況。 一些常規流程(例如,循環和異常處理)在傳統異步代碼中處理起來可能很困難。 在異步方法中,元素的編寫頻率與同步解決方案相同且此問題得到解決。

異步程序中的控制流


異步編程中最需弄清的是控制流是如何從方法移動到方法。

private async void StartButton_Click(object sender, RoutedEventArgs e)
       {
           // Call and await separately.
           //Task<int> getLengthTask = AccessTheWebAsync();
           //// You can do independent work here.
           //int contentLength = await getLengthTask;
           resultsTextBox.Text += "1:  Entering startButton_Click./r/n" +
               "           Calling AccessTheWebAsync./r/n";
 
           int contentLength = await AccessTheWebAsync();
 
           resultsTextBox.Text +=
               String.Format("/r/n6:   Length of the downloaded string: {0}./r/n", contentLength);
       }
 
       async Task<int> AccessTheWebAsync()
       {
           resultsTextBox.Text += "/r/n2:  Entering AccessTheWebAsync.";
 
           HttpClient client = new HttpClient();
 
           resultsTextBox.Text += "/r/n        Calling HttpClient.GetStringAsync./r/n";
 
           Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
 
           DoIndependentWork();
 
           resultsTextBox.Text += "/r/n4:  Back in startButton_Click./r/n" +
               "       Task getStringTask is started./r/n";
           string urlContents = await getStringTask;
 
           resultsTextBox.Text += "/r/n5:  Back in AccessTheWebAsync." +
               "/r/n       Task getStringTask is complete." +
               "/r/n       Processing the return statement." +
               "/r/n       Exiting from AccessTheWebAsync./r/n";
 
           return urlContents.Length;
       }
 
 
       void DoIndependentWork()
       {
           resultsTextBox.Text += "/r/n3:  Entering DoIndependentWork./r/n";
 
           resultsTextBox.Text += "/r/n        Working . . . . . . ./r/n";
       }

運行結果:

1:  Entering startButton_Click.
           Calling AccessTheWebAsync.
 
2:  Entering AccessTheWebAsync.
        Calling HttpClient.GetStringAsync.
 
3:  Entering DoIndependentWork.
 
        Working . . . . . . .
 
4:  Back in startButton_Click.
       Task getStringTask is started.
 
5:  Back in AccessTheWebAsync.
       Task getStringTask is complete.
       Processing the return statement.
       Exiting from AccessTheWebAsync.
 
6:   Length of the downloaded string: 41609.

再稍微復雜點:

private async void startButton_Click(object sender, RoutedEventArgs e)
       {
           // The display lines in the example lead you through the control shifts.
           resultsTextBox.Text += "ONE:   Entering startButton_Click./r/n" +
               "           Calling AccessTheWebAsync./r/n";
 
           Task<int> getLengthTask = AccessTheWebAsync();
 
           resultsTextBox.Text += "/r/nFOUR:  Back in startButton_Click./r/n" +
               "           Task getLengthTask is started./r/n" +
               "           About to await getLengthTask -- no caller to return to./r/n";
 
           int contentLength = await getLengthTask;
 
           resultsTextBox.Text += "/r/nSIX:   Back in startButton_Click./r/n" +
               "           Task getLengthTask is finished./r/n" +
               "           Result from AccessTheWebAsync is stored in contentLength./r/n" +
               "           About to display contentLength and exit./r/n";
 
           resultsTextBox.Text +=
               String.Format("/r/nLength of the downloaded string: {0}./r/n", contentLength);
       }
 
       async Task<int> AccessTheWebAsync()
       {
           resultsTextBox.Text += "/r/nTWO:   Entering AccessTheWebAsync.";
 
           // Declare an HttpClient object and increase the buffer size. The default
           // buffer size is 65,536.
           HttpClient client =
               new HttpClient() { MaxResponseContentBufferSize = 1000000 };
 
           resultsTextBox.Text += "/r/n           Calling HttpClient.GetStringAsync./r/n";
 
           // GetStringAsync returns a Task<string>. 
           Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
 
           resultsTextBox.Text += "/r/nTHREE: Back in AccessTheWebAsync./r/n" +
               "           Task getStringTask is started.";
 
           // AccessTheWebAsync can continue to work until getStringTask is awaited.
 
           resultsTextBox.Text +=
               "/r/n           About to await getStringTask and return a Task<int> to startButton_Click./r/n";
 
           // Retrieve the website contents when task is complete.
           string urlContents = await getStringTask;
 
           resultsTextBox.Text += "/r/nFIVE:  Back in AccessTheWebAsync." +
               "/r/n           Task getStringTask is complete." +
               "/r/n           Processing the return statement." +
               "/r/n           Exiting from AccessTheWebAsync./r/n";
 
           return urlContents.Length;
       }

運行結果:

ONE:   Entering startButton_Click.
           Calling AccessTheWebAsync.
 
TWO:   Entering AccessTheWebAsync.
           Calling HttpClient.GetStringAsync.
 
THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask and return a Task<;int> to startButton_Click.
 
FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.
 
FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.
 
SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.
 
Length of the downloaded string: 41635.

API 異步方法


如何找到像 GetStringAsync 這樣支持異步編程的方法。 .NET Framework 4.5 包含使用 async 和 await 的許多成員,它們都已“Async”為后綴和 Task 或 Task<TResult> 的返回類型。 例如,System.IO.Stream 類包含的方法 CopyToAsync、ReadAsync、WriteAsync 等方法以及同步方法 CopyTo、Read 和 Write。

線程


異步方法旨在成為非阻止操作。異步方法中的 await 表達式在等待的任務正在運行時,不會阻止當前線程。相反,表達式在繼續時,注冊方法的其余部分并將控件返回到異步方法的調用方。

async 和 await 關鍵字不會導致創建其他線程。因為異步方法不會在其自身線程上運行,因此它不需要多線程。 只有當方法處于活動狀態時,該方法將在當前同步上下文中運行并使用線程上的時間。 可以使用 Task.Run 將占用大量 CPU 的工作移到后臺線程,但是后臺線程不會幫助正在等待結果的進程變為可用狀態。

對于異步編程而言,該基于異步的方法優于幾乎每個用例中的現有方法。 具體而言,此方法比 BackgroundWorker 更適用于 IO 綁定的操作,因為此代碼更簡單且無需防止爭用條件。 結合 Task.Run 使用時,異步編程比 BackgroundWorker 更適用于 CPU 綁定的操作,因為異步編程將運行代碼的協調細節與 Task.Run 傳輸至線程池的工作區分開來。

異步和等待


如果通過 async 修飾符指定某種方法為異步方法,則可以啟用以下兩個功能。

  • 標記的異步方法可以使用 await 來指定懸掛點。await 運算符通知編譯器異步方法只有直到等待的異步過程完成才能繼續通過該點。 同時,控件返回至異步方法的調用方。 await 表達式中異步方法的掛起不能使該方法退出,并且 finally 塊不會運行。
  • 標記的異步方法本身可以通過調用它的方法等待

異步方法通常包含 await 運算符的一個或多個匹配項,但缺少 await 表達式不會導致編譯器錯誤。 如果異步方法未使用 await 運算符標記懸掛點,則該方法將作為同步方法執行,不管異步修飾符如何。編譯器將為此類方法發布一個警告。

Async 、async、Await 和 await 都是上下文關鍵字。 有關更多信息和示例,請參見以下主題:

  • async
  • await

返回類型和參數


.NET Framework 異步編程中異步方法通常返回 Task 或 Task<TResult>。 在異步方法中,await 運算符應用于通過調用另一個異步方法返回的任務。

如果方法包含 Return (Visual Basic) 或指定類型 TResult 的操作數的 return (C#) 語句,則將 Task<TResult> 指定為返回類型。

如果方法不含任何 return 語句或包含不返回操作數的 return 語句,則將 Task 用作返回類型。

下面的示例演示如何聲明并調用可返回 Task<TResult> 或 Task 的方法。

// Signature specifies Task<;TResult>
async Task<;int> TaskOfTResult_MethodAsync()
{
    int hours;
    // . . .
    // Return statement specifies an integer result.
    return hours;
}
 
// Calls to TaskOfTResult_MethodAsync
Task<;int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();
// Signature specifies Task
async Task Task_MethodAsync()
{
    // . . .
    // The method has no return statement.  
}
 
// Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync();
await returnedTask;
// or, in a single statement
await Task_MethodAsync();

每個返回的任務表示正在進行的工作。 任務可封裝有關異步進程狀態的信息,如果未成功,則最后會封裝來自進程的最終結果或進程引發的異常。

異步方法還可以是 Sub 方法 (Visual Basic) 或具有 void 返回類型 (C#)。 該返回類型主要用于定義需要 void 返回類型的事件處理程序。 異步事件處理程序通常用作異步程序的起始點。

無法等待為 Sub 程序或具有 void 返回類型的異步方法,并且無效的返回方法的調用方無法捕獲該方法引發的任何異常。

異步方法無法聲明 Visual Basic 中的 ByRef 參數或 C# 中的 ref 或 out 參數,但此方法可以調用具有此類參數的方法。

有關更多信息和示例,請參見異步返回類型(C# 和 Visual Basic)。 有關如何在異步方法中捕捉異常的更多信息,請參見 try-catch(C# 參考)或 Try...Catch...Finally 語句 (Visual Basic)。

Windows 運行時編程中的異步 API 具有下列返回類型之一,它類似于任務:

  • IAsyncOperation,它對應于 Task<TResult>
  • IAsyncAction,它對應于 Task
  • IAsyncActionWithProgress
  • IAsyncOperationWithProgress

參考資料


  • Microsoft Developer Network 基于任務的異步模式 (TAP)
  • 使用 Async 和 Await 的異步編程
  • 異步程序中的控制流

 

下載 Demo

下載 Demo TPL 與 AMP 和 EAP 結合


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 海口市| 伽师县| 南溪县| 香港| 石河子市| 依安县| 桦甸市| 海城市| 丹巴县| 焉耆| 尤溪县| 酒泉市| 景德镇市| 车致| 阜新市| 巴楚县| 吴川市| 惠来县| 长沙市| 临沭县| 赤城县| 阳西县| 尤溪县| 福建省| 永福县| 枣庄市| 东乡族自治县| 泰安市| 曲沃县| 景德镇市| 中阳县| 桑植县| 商南县| 深州市| 浑源县| 商丘市| 鸡西市| 道孚县| 云霄县| 从江县| 永寿县|