TAP 是基於任務的非同步模式,在 .NET Framework 4 中引入。TAP取代了 APM 和EAP,是推薦的非同步編程模式。 async / await async 和 await 是為非同步編程提供的語法糖,方便我們快捷編寫非同步代碼。關鍵字 async 作用僅僅是為了能夠使用 await 關鍵 ...
TAP 是基於任務的非同步模式,在 .NET Framework 4 中引入。TAP取代了 APM 和EAP,是推薦的非同步編程模式。
async / await
async 和 await 是為非同步編程提供的語法糖,方便我們快捷編寫非同步代碼。關鍵字 async 作用僅僅是為了能夠使用 await 關鍵字以及怎麼處理返回值。await 關鍵字可以想象成 asynchronous wait,在awaitable 完成之前,非同步方法會等待,但線程不會堵塞。
public async Task DoSomethingAsync()
{
// For this example, we`re just going to (aynchronously) wait 100ms.
await Task.Delay(100);
}
對於調用方法,await 聲明瞭一個掛起點,等非同步方法結束後會捕獲當前上下文繼續執行後續代碼。、
awaitable
await 就像是一元操作符,接收一個參數 - awaitable. Task<T> 和 Task 都是這樣的類型。
public async Task NewStuffAsync()
{
// Use await and have fun with the new stuff.
await ...
}
public Task MyOldTaskParallelLibraryCode()
{
// Note that this is not an async method, so we can`t use await in here.
...
}
public async Task ComposeAsync()
{
// We can await Tasks, regardless of where they come from.
await NewStuffAsync();
await MyOldTaskParallelLibraryCode();
}
Task.Yield()
await Task.Yield() 方法來強制非同步完成方法,可以讓我們更好的控制非同步方法的執行。如果當前任務很耗時,並且優先順序比較低,可以考慮在方法開始的時候加上 await Task.Yield() ,讓系統去調度其他更需要的任務,稍後再來完成該耗時任務。
static async Task Process()
{
await Task.Yield();
var tcs = new TaskCompletionSource<bool>();
Task.Run(() =>
{
Thread.Sleep(1000);
tcs.SetResult(true);
});
tcs.Task.Wait();
}
我不著急,我到後面從新排隊去,你先去處理其他任務吧。其實是利用 await 實現線程的切換。
Task.ConfigureAwait
預設情況,非同步方法結束後會捕獲和回覆當前上下文。如果你不關係延續上下文,可以使用 Task.ConfigureAwait 指示不要回覆而是繼續執行等待的任務。
await someTask.ConfigureAwait(continueOnCapturedContext:false);
CancellationTokenSource
從 .NET Framework 4 開始,TAP 方法支持取消操作。
var cts = new CancellationTokenSource();
string result = await DownloadStringTaskAsync(url, cts.Token);
… // at some point later, potentially on another thread
cts.Cancel();
// 取消多個非同步調用
var cts = new CancellationTokenSource();
IList<string> results = await Task.WhenAll(from url in urls select DownloadStringTaskAsync(url, cts.Token));
// at some point later, potentially on another thread
…
cts.Cancel();
Progress
通過 Progress 可以監控非同步方法的執行進度。
private async void btnDownload_Click(object sender, RoutedEventArgs e)
{
btnDownload.IsEnabled = false;
try
{
txtResult.Text = await DownloadStringTaskAsync(txtUrl.Text,
new Progress<int>(p => pbDownloadProgress.Value = p));
}
finally { btnDownload.IsEnabled = true; }
}
Task.Run
Task.Run() 方法可以很方便的將耗時任務放到線程池上執行。
public async void button1_Click(object sender, EventArgs e)
{
// 預設恢覆上下文
textBox1.Text = await Task.Run(() =>
{
// … do compute-bound work here
return answer;
});
}
public async void button1_Click(object sender, EventArgs e)
{
// 內部使用 await
pictureBox1.Image = await Task.Run(async() =>
{
using(Bitmap bmp1 = await DownloadFirstImageAsync())
using(Bitmap bmp2 = await DownloadSecondImageAsync())
return Mashup(bmp1, bmp2);
});
}
Task.FromResult
Task.FromResult 用來創建一個帶返回值的,已完成的 Task。
public Task<int> GetValueAsync(string key)
{
int cachedValue;
return TryGetCachedValue(out cachedValue) ?
Task.FromResult(cachedValue) : // 如果本地有緩存,直接以同步的方式獲取(但返回的是非同步結果)
GetValueAsyncInternal(); // 如果本地沒有key對應的緩存,則非同步從遠端獲取
}
// 非同步方法從遠端獲取緩存
private async Task<int> GetValueAsyncInternal(string key)
{
…
}
Task.WhenAll
非同步等待 一組非同步操作的完成。
Task [] asyncOps = (from addr in addrs select SendMailAsync(addr)).ToArray();
try
{
await Task.WhenAll(asyncOps);
}
catch(Exception exc)
{
foreach(Task faulted in asyncOps.Where(t => t.IsFaulted))
{
… // work with faulted and faulted.Exception
}
}
Task.WhenAny
一組非同步操作中,第一個非同步操作完成時返回。
- 可以同時進行多個相同的非同步操作,選擇最快完成的那個
// 從多個行情源處獲取行情,使用最快的那個
var cts = new CancellationTokenSource();
var recommendations = new List<Task<bool>>()
{
GetBuyRecommendation1Async(symbol, cts.Token),
GetBuyRecommendation2Async(symbol, cts.Token),
GetBuyRecommendation3Async(symbol, cts.Token)
};
Task<bool> recommendation = await Task.WhenAny(recommendations);
cts.Cancel(); // 取消剩餘任務
if (await recommendation) BuyStock(symbol);
- 多個任務交叉進行(每完成一個就處理一個)
List<Task<Bitmap>> imageTasks =
(from imageUrl in urls select GetBitmapAsync(imageUrl)
.ContinueWith(t => ConvertImage(t.Result)).ToList();
while(imageTasks.Count > 0)
{
try
{
Task<Bitmap> imageTask = await Task.WhenAny(imageTasks);
imageTasks.Remove(imageTask);
Bitmap image = await imageTask;
panel.AddImage(image);
}
catch{}
}
Task.Delay
在非同步方法中暫定一段時間。 可以和 Task.WhenAny ,Task.WhenAll 結合使用以實現超時處理。
public async void btnDownload_Click(object sender, EventArgs e)
{
btnDownload.Enabled = false;
try
{
Task<Bitmap> download = GetBitmapAsync(url);
if (download == await Task.WhenAny(download, Task.Delay(3000)))
{
Bitmap bmp = await download;
pictureBox.Image = bmp;
status.Text = "Downloaded";
}
else
{
pictureBox.Image = null;
status.Text = "Timed out";
var ignored = download.ContinueWith(
t => Trace("Task finally completed"));
}
}
finally { btnDownload.Enabled = true; }
}