[C#.NET][TPL] 建立 TAP 非同步模型
.NET 4.0 後導入了 TPL (Task Parallel Library) ,Jeffrey Richter 在 CLR via C#, Third Edition 書中則建議開發人員使用 TPL 改寫 EAP / APM 兩種非同步模式。
.NET 4.5 在 TPL 基礎建設下發展了非同步模型 TAP(Task-based Asynchronous Pattern),由下圖得知,FCL 新增許多 Async 為後綴詞的方法,搭配 async / await 關鍵字,讓開發人員大大減少了開發非同步的成本。
下圖出處為:CLR via C#, Fourth Edition
建立 TAP 模組有幾個要點:
- 命名規則以 Async 為後綴,比如 ReadAsync
- 返回 Task 或是 Task<TResult>,調用Task.Run / Task.Factory.StartNew 方法
- 若需要取消機制使用 CancellationToken
- 若需要回報機制使用 IProgress<T>,內部已經幫我們使用 SynchronizationContext 處理掉 UI 跨執行緒的問題,有關 SynchronizationContext 詳細用法可參考 http://www.dotblogs.com.tw/yc421206/archive/2013/06/14/105514.aspx
接下來我們來看看以下的片段程式碼,StartPingAsync 方法是無窮循環的 Ping,包含了取消以及回報機制
public Task StartPingAsync(PingConfig pingConfig, CancellationToken cancellationToken, IProgress<string> progress) { if (this.IsStartPooling) { return null; } this.IsStartPooling = true; return Task.Factory.StartNew(() => { while (true) { if (!this.IsStartPooling) { break; } if (cancellationToken.IsCancellationRequested) { break; } var result = GetPing(pingConfig.Address, pingConfig.TimeOut, pingConfig.BufferLength); if (progress != null) { progress.Report(result); } SpinWait.SpinUntil(() => !this.IsStartPooling || cancellationToken.IsCancellationRequested, pingConfig.Interval); } }, cancellationToken); }
用戶端調用,在此就不必再處理跨執行緒的問題
private void button_StartAsync_Click(object sender, EventArgs e) { var progress = new Progress<string>(); progress.ProgressChanged += (o, result) => { this.listBox_PingResult.Items.Add(result); }; this._pooling.StartPingAsync(this._pingConfig, this._cts.Token, progress); }
假若 StartPingAsync 任務後面還有任務要做,比如例外的捕捉或是任務取消後要處理的動作,我們可以加上 async / await 關鍵字,在此就不說明怎麼處理取消跟例外,為了觀察變化我增加了 MessageBox.Show("Finish"),我們會發現,MessageBox.Show("Finish")不會立即執行,除非按下 "Stop" 按鈕。
private async void button_StartAsync_Click(object sender, EventArgs e) { var progress = new Progress<string>(); progress.ProgressChanged += async (o, result) => { this.listBox_PingResult.Items.Add(result); }; this._cts = new CancellationTokenSource(); var task = this._pooling.StartPingAsync(this._pingConfig, this._cts.Token, progress); await task; //TODO:可補捉任務例外或任務取消例外 MessageBox.Show("Finish"); }
執行畫面如下:
範例下載:https://dotblogsfile.blob.core.windows.net/user/yc421206/1308/20138516536965.zip
以上內容出自:http://www.dotblogs.com.tw/yc421206/archive/2013/08/05/113433.aspx
若有謬誤,煩請告知,新手發帖請多包涵
Microsoft MVP Award 2010~2017 C# 第四季
Microsoft MVP Award 2018~2022 .NET