[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