1. 程式人生 > 實用技巧 >Docs-.NET-.NET-指南-非同步程式設計模式:基於任務的非同步模式

Docs-.NET-.NET-指南-非同步程式設計模式:基於任務的非同步模式

ylbtech-Docs-.NET-.NET-指南-非同步程式設計模式:基於任務的非同步模式

1.返回頂部
1、

基於任務的非同步模式

基於任務的非同步模式 (TAP) 是基於System.Threading.Tasks名稱空間中的System.Threading.Tasks.TaskSystem.Threading.Tasks.Task<TResult>型別,這些型別用於表示任意非同步操作。TAP 是用於新開發的建議的非同步設計模式。

命名、引數和返回型別

TAP 使用單個方法表示非同步操作的開始和完成這與非同步程式設計模型(APM 或IAsyncResult

)模式和基於事件的非同步模式 (EAP) 形成對比。APM 需要BeginEnd方法EAP 需要字尾為Async的方法,以及一個或多個事件、事件處理程式委託型別和EventArg派生型別TAP 中的非同步方法在返回可等待型別(如TaskTask<TResult>ValueTaskValueTask<TResult>)的方法的操作名稱後面新增Async字尾。例如,返回Task<String>的非同步Get操作可命名為GetAsync若要將 TAP 方法新增到已包含帶Async字尾的 EAP 方法名稱的類中,請改用字尾TaskAsync例如,如果類具有GetAsync
方法,請使用名稱GetTaskAsync如果方法啟動非同步操作,但不返回可等待型別,它的名稱應以BeginStart或表明此方法不返回或丟擲操作結果的其他某謂詞開頭。

TAP 方法返回System.Threading.Tasks.TaskSystem.Threading.Tasks.Task<TResult>,具體取決於相應同步方法返回的是 void 還是型別TResult

TAP 方法的引數應與其同步對應方法的引數匹配,並應以相同順序提供。但是,outref引數不受此規則的限制,並應完全避免。應該將通過outref引數返回的所有資料改為作為由TResult返回的Task<TResult>

的一部分返回,且應使用元組或自定義資料結構來容納多個值即使 TAP 方法的同步對應項沒有提供CancellationToken引數,也應考慮新增此引數。

專用於建立、控制或組合任務的方法無需遵循此命名模式,因為方法名稱或方法所屬型別的名稱已明確指明方法的非同步用途;此類方法通常稱為“組合器”。組合器的示例包括WhenAllWhenAny使用基於任務的非同步模式一文的使用基於任務的內建組合器部分對此進行了介紹。

有關展示了 TAP 語法與舊非同步程式設計模式(如非同步程式設計模型 (APM) 和基於事件的非同步模式 (EAP))語法區別的示例,請參閱非同步程式設計模式

初始化非同步操作

基於 TAP 的非同步方法可以同步完成少量工作,如在返回結果任務之前,驗證自變數和啟動非同步操作。應將同步工作保持最小,以便非同步方法可以快速返回。快速返回的原因包括:

  • 可以從使用者介面 (UI) 執行緒呼叫非同步方法,因此,所有長期執行的同步工作可能會降低應用程式的響應能力

  • 可以同時啟動多個非同步方法因此,在非同步方法的同步部分中的任何長時間執行的工作都可以延遲其他非同步操作的啟動,從而減少併發的優點

在某些情況下,完成操作所需的工作量要比非同步啟動操作所需的工作量少。讀取流時,按照在記憶體中已緩衝好的資料來滿足該讀取,這就是此類情形的一個示例在這樣的情況下,操作可能會同步完成,同時返回已完成的任務。

異常

非同步方法應引發僅將引發非同步方法呼叫的異常,以響應用法錯誤。用法錯誤決不應該出現在成品程式碼中。例如,如果將空引用(在 Visual Basic 中為Nothing)作為方法的某個引數傳遞導致了錯誤狀態(通常由ArgumentNullException異常表示),則可以修改呼叫程式碼以確保絕對不傳遞空引用。對於所有其他錯誤,在執行非同步方法時發生的異常應分配給返回的任務,即使該非同步方法碰巧在任務返回前同步完成。通常,任務最多包含一個異常。但是,如果任務表示多個操作(例如,WhenAll),則多個異常可能與單個任務關聯。

目標環境

在實現 TAP 方法時,你可以確定非同步執行發生的位置可選擇線上程池上執行工作負載通過使用非同步 I/O 實現它(不必繫結到大部分操作執行的執行緒)在特定執行緒(如 UI 執行緒)上執行它,或者使用任何數目的潛在上下文TAP 方法甚至可能沒有要執行的程式碼,可能只返回Task表示系統其他位置發生的情況(例如,表示到達已排入佇列資料結構的資料的任務)。

TAP 方法的呼叫方可能會同步等待生成的任務,以阻止等待 TAP 方法完成,也可能會在非同步操作完成時執行其他(延續)程式碼。延續程式碼的建立者可以控制該程式碼的執行位置。你可以通過Task類上的方法(例如,ContinueWith)顯式建立延續程式碼,也可以使用基於延續(例如,C# 中的await、Visual Basic 中的Await和 F# 中的AwaitValue)構建的語言支援隱式建立延續程式碼。

任務狀態

Task類提供了非同步操作的生命週期,且該週期由TaskStatus列舉表示。為了支援派生自TaskTask<TResult>的型別的個別案例,並支援排程時分離構造Task類公開了Start方法。公共Task建構函式建立的任務稱為“冷任務”因為它們在非計劃Created狀態下開始生命週期,並僅在對這些例項呼叫Start時才被排入計劃

所有其他任務在熱狀態下開始其生命週期,這意味著它們表示的非同步操作已啟動,並且其任務狀態是TaskStatus.Created以外的列舉值必須啟用從 TAP 方法返回的所有任務。如果 TAP 方法在內部使用任務的建構函式來例項化要返回的任務,TAP 方法必須在返回前先對Task物件呼叫StartTAP 方法的使用者可以安全地假設返回的任務處於活動狀態且不應嘗試對從 TAP 方法返回的任何Start呼叫Task對活動的任務呼叫Start將引發InvalidOperationException異常。

取消(可選)

在 TAP 中,取消是非同步方法實現者和非同步方法使用者的選項。如果操作允許取消,則會公開接受取消標記(CancellationToken例項)的非同步方法的過載。按照約定,該引數命名為cancellationToken

C#
public Task ReadAsync(byte [] buffer, int offset, int count,
                      CancellationToken cancellationToken)

該非同步操作監視取消請求的此標記。如果它收到取消請求,則可以選擇接受該請求並取消操作。如果取消請求導致過早地結束工作,則 TAP 方法返回一個在Canceled狀態下結束的任務;沒有可用結果且不引發異常。Canceled狀態被視為任務的最終(完成)狀態,以及FaultedRanToCompletion狀態。因此,如果一個任務處於Canceled狀態,則其IsCompleted屬性將返回trueCanceled狀態下完成任務時,將計劃或執行向任務註冊的任何延續,除非延續選項(如NotOnCanceled)特定於取消延續。任何通過使用語言功能非同步等待已取消的任務的程式碼都將繼續執行,但不接收OperationCanceledException或其中派生的異常。通過諸如Wait的方法同步阻止的程式碼等待任務,並且WaitAll將繼續執行但出現異常。

如果取消標記請求在接受呼叫標記的 TAP 方法之前取消,TAP 方法應返回Canceled任務。但是,如果在執行非同步操作時請求取消,則非同步操作不需要接受該取消請求。僅當該操作如取消請求的結果那樣結束時,返回的任務才應以Canceled狀態結束。如果已請求取消,但仍然生成了結果或異常,則任務應在RanToCompletionFaulted狀態下結束。

對於要首先對其公開可取消功能的非同步方法,無需提供不接受取消令牌的過載。對於無法取消的方法,不提供接受取消標記的過載;這有助於向呼叫方指示目標方法是否真正可取消。不要求取消的使用者程式碼可以呼叫接受CancellationToken的方法,並提供None作為引數值。None在功能上等效於預設CancellationToken

進度報告(可選)

某些非同步操作受益於提供進度通知這些通常用於使用有關該非同步操作的進度的資訊更新使用者介面

在 TAP 中,通過IProgress<T>介面處理進度,此介面作為通常名為progress的引數傳遞給非同步方法。呼叫非同步方法時提供進度介面有助於消除不正確使用導致的爭用情況(也就是說,操作啟動後未正確註冊的事件處理程式可能缺少更新)。更重要的是,根據所使用的程式碼,進度介面將支援不同的進度實現。例如,使用程式碼可能只關心最新的進度更新,可能需要緩衝所有更新,可能需要為各個更新呼叫操作,也可能需要控制是否需將該呼叫封送到特定執行緒。通過使用介面的不同實現,所有這些選項都可以滿足特定使用方的需要。與取消一樣,僅在 API 支援進度通知時,TAP 實現才應提供IProgress<T>引數。

例如,如果本文前面所述的ReadAsync方法可以以到目前為止讀取的位元組數的形式報告中間進度,則進度回撥可能為IProgress<T>介面:

C#
public Task ReadAsync(byte[] buffer, int offset, int count,
                      IProgress<long> progress)

如果FindFilesAsync方法返回滿足特定搜尋模式的所有檔案的列表,則進度回叫可以對已完成工作的百分比和當前部分結果集進行估計可通過元組來提供此資訊:

C#
public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
            string pattern,
            IProgress<Tuple<double,
            ReadOnlyCollection<List<FileInfo>>>> progress)

也可以使用特定於 API 的資料型別執行此操作:

C#
public Task<ReadOnlyCollection<FileInfo>> FindFilesAsync(
    string pattern,
    IProgress<FindFilesProgressInfo> progress)

在後一種情況下,特殊資料型別應加上字尾ProgressInfo

如果 TAP 實現提供接受progress引數的過載,則必須允許該引數為null,在這種情況下,不會報告任何進度。TAP 實現應向Progress<T>物件同步報告進度,這使非同步方法能夠快速提供進度。它還允許進度使用方確定處理資訊的最佳方式和位置。例如,進度例項可以選擇將回調封送,並引發有關捕獲到的同步上下文的事件。

IProgress<T> 實現

.NET Framework 4.5 提供單個IProgress<T>實現:Progress<T>Progress<T>類的宣告方式如下:

C#
public class Progress<T> : IProgress<T>  
{  
    public Progress();  
    public Progress(Action<T> handler);  
    protected virtual void OnReport(T value);  
    public event EventHandler<T> ProgressChanged;  
}  

Progress<T>的例項公開ProgressChanged事件,此事件在非同步操作每次報告進度更新時引發。例項化ProgressChanged例項後,會在捕獲到的SynchronizationContext物件上引發Progress<T>事件。如果沒有可用的同步上下文,則使用針對執行緒池的預設上下文可以向此事件註冊處理程式。為了方便起見,也可將單個處理程式提供給Progress<T>建構函式,並且行為與ProgressChanged事件的事件處理程式一樣。非同步引發進度更新以避免延遲非同步操作,同時執行事件處理程式。另一個IProgress<T>實現可以選擇應用不同的語義。

選擇要提供的過載

如果 TAP 實現使用可選的CancellationToken和可選的IProgress<T>引數,則可能需要多達四次的過載:

C#
public Task MethodNameAsync(…);  
public Task MethodNameAsync(…, CancellationToken cancellationToken);  
public Task MethodNameAsync(…, IProgress<T> progress);
public Task MethodNameAsync(…,
    CancellationToken cancellationToken, IProgress<T> progress);  

但是,許多 TAP 實現沒有提供取消和進度功能,因此它們需要一個方法:

C#
public Task MethodNameAsync(…);  

如果 TAP 實現支援取消或進度但不同時支援二者,則 TAP 實現可能提供以下兩種過載

C#
public Task MethodNameAsync(…);  
public Task MethodNameAsync(…, CancellationToken cancellationToken);  
  
// … or …  
  
public Task MethodNameAsync(…);  
public Task MethodNameAsync(…, IProgress<T> progress);  

如果 TAP 實現同時支援取消和進度,則可以公開所有四個過載。但它也可以只提供以下兩個:

C#
public Task MethodNameAsync(…);  
public Task MethodNameAsync(…,
    CancellationToken cancellationToken, IProgress<T> progress);  

若要彌補缺少的兩個中間組合,開發人員可以為cancellationToken引數傳遞None或預設的CancellationToken,為null引數傳遞progress

如果需要 TAP 方法的每種用法都支援取消或進度,則可以忽略不接受相關引數的過載。

如果決定公開多個過載以使取消或進度可選,則不支援取消或進度的過載的行為方式就像其已將取消的None或進度的null傳遞給確實支援它們的過載。

相關文章
Title描述
非同步程式設計模式 介紹執行非同步操作的三種模式基於任務的非同步模式 (TAP)、非同步程式設計模型 (APM) 和基於事件的非同步模式 (EAP)
實現基於任務的非同步模式 描述如何使用以下三種方式實現基於任務的非同步模式 (TAP):手動使用 Visual Studio 中的 C# 和 Visual Basic 編譯器,或通過編譯器和手動方法的組合
使用基於任務的非同步模式 描述你可以如何使用任務和回撥實現等待,而無需阻止
與其他非同步模式和型別互操作 描述如何使用基於任務的非同步模式 (TAP) 實現非同步程式設計模型 (APM) 和基於事件的非同步模式 (EAP)。
2、
2.返回頂部
3.返回頂部
4.返回頂部
5.返回頂部
1、 https://docs.microsoft.com/zh-cn/dotnet/standard/asynchronous-programming-patterns/task-based-asynchronous-pattern-tap?view=netframework-4.7.2 2、
6.返回頂部
作者:ylbtech
出處:http://ylbtech.cnblogs.com/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。