[C#.NET][TPL] 建立 TAP 非同步模型

[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

image

 

建立 TAP 模組有幾個要點:

  1. 命名規則以 Async 為後綴,比如 ReadAsync
  2. 返回 Task 或是 Task<TResult>,調用Task.Run / Task.Factory.StartNew 方法
  3. 若需要取消機制使用 CancellationToken
  4. 若需要回報機制使用 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");
}


執行畫面如下:

 

SNAGHTML10049033

 


 

範例下載: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

Image result for microsoft+mvp+logo