.NET4.0版本中基于任務(wù)的異步模式(TAP)
一、引言
當(dāng)使用APM的時(shí)候,首先我們要先定義用來包裝回調(diào)方法的委托,這樣難免有點(diǎn)繁瑣, 然而使用EAP的時(shí)候,我們又需要實(shí)現(xiàn)Completed事件和Progress事件,上面兩種實(shí)現(xiàn)方式感覺都有點(diǎn)繁瑣。
同時(shí)微軟也意識(shí)到了這點(diǎn),所以在.NET 4.0中提出了一個(gè)新的異步模式——基于任務(wù)的異步模式TAP(Task-based Asynchronous Pattern )。
基于任務(wù)的異步模式 (TAP) 是基于 System.Threading.Tasks.Task 命名空間中的 System.Threading.Tasks.Task 和 System.Threading.Tasks類型,這些類型用于表示任意異步操作。是用于新開發(fā)的建議的異步設(shè)計(jì)模式。
二、什么是TAP——基于任務(wù)的異步模式介紹
當(dāng)看到類中存在TaskAsync為后綴的方法時(shí)就代表該類實(shí)現(xiàn)了TAP, 并且基于任務(wù)的異步模式同樣也支持異步操作的取消和進(jìn)度的報(bào)告的功能。
在TAP實(shí)現(xiàn)中,我們只需要通過向異步方法傳入CancellationToken 參數(shù),因?yàn)樵诋惒椒椒▋?nèi)部會(huì)對(duì)這個(gè)參數(shù)的IsCancellationRequested屬性進(jìn)行監(jiān)控,當(dāng)異步方法收到一個(gè)取消請(qǐng)求時(shí),異步方法將會(huì)退出執(zhí)行。
在TAP中,我們可以通過IProgress接口來實(shí)現(xiàn)進(jìn)度報(bào)告的功能。
1、計(jì)算密集型任務(wù)
例如,請(qǐng)考慮使用呈現(xiàn)圖像的異步方法。
任務(wù)的主體可以輪詢?nèi)∠麡?biāo)記,如果在呈現(xiàn)過程中收到取消請(qǐng)求,代碼可提前退出。 此外,如果啟動(dòng)之前收到取消請(qǐng)求,你需要阻止操作:
internal Task RenderAsync(ImageData data, CancellationToken cancellationToken)
{
return Task.Run(() =>
{
var bmp = new Bitmap(data.Width, data.Height);
for(int y=0; y)
{
cancellationToken.ThrowIfCancellationRequested();
for(int x=0; x)
{
// render pixel [x,y] into bmp
}
}
return bmp;
}, cancellationToken);
}2、I/O 密集型任務(wù):
假設(shè)你想創(chuàng)建一個(gè)將在指定時(shí)間段后完成的任務(wù)。 例如,你可能想延遲用戶界面中的活動(dòng)。
System.Threading.Timer 類已提供在指定時(shí)間段后以異步方式調(diào)用委托的能力,并且你可以通過使用 TaskCompletionSource 將 Task 前端放在計(jì)時(shí)器上,例如:
public static Task Delay(int millisecondsTimeout)
{
TaskCompletionSource tcs = null;
Timer timer = null;
timer = new Timer(delegate
{
timer.Dispose();
tcs.TrySetResult(DateTimeOffset.UtcNow);
}, null, Timeout.Infinite, Timeout.Infinite);
tcs = new TaskCompletionSource(timer);
timer.Change(millisecondsTimeout, Timeout.Infinite);
return tcs.Task;
}從 .NET Framework 4.5 開始,Task.Delay 方法正是為此而提供的,并且你可以在另一個(gè)異步方法內(nèi)使用它。例如,若要實(shí)現(xiàn)異步輪詢循環(huán):
public static async Task Poll(Uri url, CancellationToken cancellationToken, IProgress<bool> progress)
{
while(true)
{
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken);
bool success = false;
try
{
await DownloadStringAsync(url);
success = true;
}
catch { /* ignore errors */ }
progress.Report(success);
}
}三、如何使用TAP——使用基于任務(wù)的異步模式來異步編程
下面就讓我們實(shí)現(xiàn)自己的異步方法(亮點(diǎn)為只需要一個(gè)方法就可以完成進(jìn)度報(bào)告和異步操作取消的功能)
// Download File CancellationToken 參數(shù)賦值獲得一個(gè)取消請(qǐng)求 progress參數(shù)負(fù)責(zé)進(jìn)度報(bào)告
private void DownLoadFile(string url, CancellationToken ct, IProgress<int> progress)
{
HttpWebRequest request = null;
HttpWebResponse response = null;
Stream responseStream = null;
int bufferSize = 2048;
byte[] bufferBytes = new byte[bufferSize];
try
{
request = (HttpWebRequest)WebRequest.Create(url);
if (DownloadSize != 0)
{
request.AddRange(DownloadSize);
}
response = (HttpWebResponse)request.GetResponse();
responseStream = response.GetResponseStream();
int readSize = 0;
while (true)
{
// 收到取消請(qǐng)求則退出異步操作
if (ct.IsCancellationRequested == true)
{
MessageBox.Show(String.Format("下載暫停,下載的文件地址為:{0}\n 已經(jīng)下載的字節(jié)數(shù)為: {1}字節(jié)", downloadPath, DownloadSize));
response.Close();
filestream.Close();
sc.Post((state) =>
{
this.btnStart.Enabled = true;
this.btnPause.Enabled = false;
}, null);
// 退出異步操作
break;
}
readSize = responseStream.Read(bufferBytes, 0, bufferBytes.Length);
if (readSize > 0)
{
DownloadSize += readSize;
int percentComplete = (int)((float)DownloadSize / (float)totalSize * 100);
filestream.Write(bufferBytes, 0, readSize);
// 報(bào)告進(jìn)度
progress.Report(percentComplete);
}
else
{
MessageBox.Show(String.Format("下載已完成,下載的文件地址為:{0},文件的總字節(jié)數(shù)為: {1}字節(jié)", downloadPath, totalSize));
sc.Post((state) =>
{
this.btnStart.Enabled = false;
this.btnPause.Enabled = false;
}, null);
response.Close();
filestream.Close();
break;
}
}
}
catch (AggregateException ex)
{
// 因?yàn)檎{(diào)用Cancel方法會(huì)拋出OperationCanceledException異常 將任何OperationCanceledException對(duì)象都視為以處理
ex.Handle(e => e is OperationCanceledException);
}
}這樣只需要上面的一個(gè)方法,我們就可以完成上一專題中文件下載的程序,我們只需要在下載按鈕的事件處理程序調(diào)用該方法和在暫停按鈕的事件處理程序調(diào)用CancellationTokenSource.Cancel方法即可,具體代碼為:
#region 字段
private int DownloadSize = 0;
private string downloadPath = null;
private long totalSize = 0;
private FileStream filestream;
private CancellationTokenSource cts = null;
private Task task = null;
private SynchronizationContext sc;
#endregion 字段
#region 構(gòu)造函數(shù)
public FileDownLoadForm()
{
InitializeComponent();
string url = "http://download.microsoft.com/download/7/0/3/703455ee-a747-4cc8-bd3e-98a615c3aedb/dotNetFx35setup.exe";
txbUrl.Text = url;
this.btnPause.Enabled = false;
// Get Total Size of the download file
GetTotalSize();
downloadPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\" + Path.GetFileName(this.txbUrl.Text.Trim());
if (File.Exists(downloadPath))
{
FileInfo fileInfo = new FileInfo(downloadPath);
DownloadSize = (int)fileInfo.Length;
if (DownloadSize == totalSize)
{
string message = "There is already a file with the same name, do you want to delete it? If not, please change the local path. ";
var result = MessageBox.Show(message, "File name conflict: " + downloadPath, MessageBoxButtons.OKCancel);
if (result == System.Windows.Forms.DialogResult.OK)
{
File.Delete(downloadPath);
}
else
{
progressBar1.Value = (int)((float)DownloadSize / (float)totalSize * 100);
this.btnStart.Enabled = false;
this.btnPause.Enabled = false;
}
}
}
}
#endregion 構(gòu)造函數(shù)
#region 方法
// Start DownLoad File
private void btnStart_Click(object sender, EventArgs e)
{
filestream = new FileStream(downloadPath, FileMode.OpenOrCreate);
this.btnStart.Enabled = false;
this.btnPause.Enabled = true;
filestream.Seek(DownloadSize, SeekOrigin.Begin);
// 捕捉調(diào)用線程的同步上下文派生對(duì)象
sc = SynchronizationContext.Current;
cts = new CancellationTokenSource();
// 使用指定的操作初始化新的 Task。
task = new Task(() => Actionmethod(cts.Token), cts.Token);
// 啟動(dòng) Task,并將它安排到當(dāng)前的 TaskScheduler 中執(zhí)行。
task.Start();
//await DownLoadFileAsync(txbUrl.Text.Trim(), cts.Token,new Progress(p => progressBar1.Value = p));
}
// 任務(wù)中執(zhí)行的方法
private void Actionmethod(CancellationToken ct)
{
// 使用同步上文文的Post方法把更新UI的方法讓主線程執(zhí)行
DownLoadFile(txbUrl.Text.Trim(), ct, new Progress<int>(p =>
{
sc.Post(new SendOrPostCallback((result) => progressBar1.Value = (int)result), p);
}));
}
// Pause Download
private void btnPause_Click(object sender, EventArgs e)
{
// 發(fā)出一個(gè)取消請(qǐng)求
cts.Cancel();
}
// Get Total Size of File
private void GetTotalSize()
{
HttpWebRequest myHttpWebRequest = (HttpWebRequest)WebRequest.Create(txbUrl.Text.Trim());
HttpWebResponse response = (HttpWebResponse)myHttpWebRequest.GetResponse();
totalSize = response.ContentLength;
response.Close();
}四、與其他異步模式和類型互操作
1、從 APM 到 TAP
可以使用 TaskFactory.FromAsync 方法來實(shí)現(xiàn)此操作的 TAP 包裝,如下所示:
public static Task<int> ReadAsync(this Stream stream, byte[] buffer, int offset, int count)
{
if (stream == null)
throw new ArgumentNullException("stream");
return Task<int>.Factory.FromAsync(stream.BeginRead, stream.EndRead, buffer, offset, count, null);
}此實(shí)現(xiàn)類似于以下內(nèi)容:
public static Task<int> ReadAsync(this Stream stream, byte [] buffer, int offset, int count)
{
if (stream == null)
throw new ArgumentNullException("stream");
var tcs = new TaskCompletionSource<int>();
stream.BeginRead(buffer, offset, count, iar =>
{
try {
tcs.TrySetResult(stream.EndRead(iar));
}
catch(OperationCanceledException) {
tcs.TrySetCanceled();
}
catch(Exception exc) {
tcs.TrySetException(exc);
}
}, null);
return tcs.Task;
}2、從EAP到 TAP
public static Task<string> DownloadStringAsync(Uri url)
{
var tcs = new TaskCompletionSource<string>();
var wc = new WebClient();
wc.DownloadStringCompleted += (s,e) =>
{
if (e.Error != null)
tcs.TrySetException(e.Error);
else if (e.Cancelled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(e.Result);
};
wc.DownloadStringAsync(url);
return tcs.Task;
}到此這篇關(guān)于.NET4.0異步模式(TAP)的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
ASP.NET中MultiView和View選項(xiàng)卡控件的使用方法
ASP.NET中的MultiView和View控件可以作為承載其他控件的容器,一般我們都很少使用,本文主要介紹使用MultiView和View實(shí)現(xiàn)選項(xiàng)卡效果。2016-04-04
SQL Server 2005 RTM 安裝錯(cuò)誤 :The SQL Server System Configuratio
SQL Server 2005 RTM 安裝錯(cuò)誤 :The SQL Server System Configuration Checker cannot be executed due to...2007-02-02
.NET?6中System.Text.Json的七個(gè)特性
這篇文章介紹了.NET?6中System.Text.Json的七個(gè)特性,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-01-01

