內容預告:
- awaitable的對象如何工作
- 並行處理的新方法
- 用async替代BackgroundWorkder
- 了解為什么不再使用線程和線程池
異步函數與基於任務的編程模型(TPM==Task Programming Model):
- WP7.1用.NET4.0的模式支持異步編程:BeginXYZ,EndXYZ函數,如HttpWebRequest.BeginGetResponse, EndGetResponse。
- WP7.1異步事件模型:設置一個Completed事件處理器,然后用XYZAsync調用操作,如WebClient.DownloadAsync函數和DownloadCompleted enent。
- WP8中任何操作時間可能超過50毫秒的任務都以TPM的方式暴露為一個異步函數。
- 在WinRT中異步編程很重要,文件IO,網絡傳輸都在用它。
- TPM逐漸成為編寫異步代碼的方式。
- WP8中很多新的API都會提供一個基於任務的API。
保證UI的流暢性:比較WP7.1中阻塞式文件IO和WP8中TPM式的文件IO:
WP7.1:
var isf = IsolatedStorageFile.GetUserStoreForApplication();
using (var fs = new IsolatedStorageFileStream("CaptainsLog.store", FileMode.Open, isf)) { StreamReader reader = new StreamReader(fs); theData = reader.ReadToEnd(); reader.Close(); };
WP8:
StorageFile storageFile = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appdata:///local/CaptainsLog.store ")); Stream readStream = await StorageFile.OpenStreamForReadAsync(); using (StreamReader reader = new StreamReader(readStream)) { theData = await reader.ReadToEndAsync(); }
上異步代碼看起來同步:
編譯器做的轉換:在一個函數的簽名中加上async會使編譯器用狀態機重新實現這個函數。使用狀態機編譯器可以在函數扶起和恢復的地方插入一個標記點,而不是阻止線程執行。這些點只會在你顯示地用await關鍵字的時候插入。當你await一個異步操作的時候,以下事情都還沒有做:
- 編譯器打包所有當前的調用函數的狀態把它們保存在堆上。
- 函數返回給調用者,允許線程干其他活。
- 當await標記的操作完成時,函數用保存的狀態繼續執行。
釋放調用線程
:
錯誤處理:繼續向同步代碼一樣用try..catch就行,當代碼異步執行時,在調用的線程上拋出異常:
private async void SomeMethod() { try { string theData = await LoadFromLocalFolder();
TextBox1.Text = theData; } catch (Exception ex) { // An exception occurred from the async operation } }
調用async標記的函數時必須帶await嗎?當然不,不帶await時,調用的函數仍然在后台線程執行,調用線程也不等待結果,async函數返回void或task,而不是Task<TResult>。當你這么做時,智能提示會警告你:
替代BackgroundWorker:如果有長時間執行的代碼可以放在后台線程里運行,BackgroundWorkder在WP8仍然支持,並可以取消和報告進度。但這些用Task也能解決。
BackroundWorker:
private void LaunchTaskButton_Click(object sender, RoutedEventArgs e) { BackgroundWorker bgw = new BackgroundWorker();
bgw.RunWorkerCompleted += ((s, a) =>MessageBox.Show("BackgroundWorker has completed, result: " + (int)a.Result)); bgw.DoWork += ((s, a) =>{ // Simulate some long running work Thread.Sleep(5000); // Return the result a.Result = 1234; }); // Now start execution bgw.RunWorkerAsync(); }
用Task實現的等效的代碼:
private async void LaunchTaskButton_Click(object sender, RoutedEventArgs e) { int result = await Task.Factory.StartNew<int>(() =>{ // Simulate some long running work
Thread.Sleep(5000); // Return the result return 4321; }); MessageBox.Show("BackgroundWorker has completed, result is: " + result); }
BackgroundWorkder的進度匯報:
BackgroundWorker bgw = new BackgroundWorker(); bgw.WorkerReportsProgress = true;
bgw.RunWorkerCompleted += ((s, a) => MessageBox.Show("BackgroundWorker has completed, result: " + (int)a.Result));
bgw.ProgressChanged += ((s, a) => { // Progress Indicator value must be between 0 and 1
SystemTray.GetProgressIndicator(this).Value = (double)a.ProgressPercentage/100.0; }); bgw.DoWork += ((s, a) => { // Simulate some long running work for (int i = 0; i < 10; i++) { Thread.Sleep(500); // Report progress as percentage completed
bgw.ReportProgress(i * 10); } a.Result = 1234;
// Return the result }); // Now start execution bgw.RunWorkerAsync();
基於Task的進度匯報:
IProgress<int> progressReporter = new Progress<int>((percentComplete) => // Progress Indicator value must be between 0 and 1
SystemTray.GetProgressIndicator(this).Value = (double)percentComplete / 100.0 ); int result = await Task.Factory.StartNew<int>(() => { // Simulate some long running work for (int i = 0; i < 10; i++) { Thread.Sleep(500); // Report progress as percentage completed
progressReporter.Report(i * 10); } // Return the result return 4321; } ); MessageBox.Show("BackgroundWorker has completed, result is: " + result);
BackgroundWorker的取消:
BackgroundWorker bgw = new BackgroundWorker();
bgw.WorkerReportsProgress = true;
bgw.WorkerSupportsCancellation = true;
bgw.RunWorkerCompleted += ((s, a) => {
if (a.Cancelled)
MessageBox.Show("BackgroundWorker was cancelled");
else
MessageBox.Show("BackgroundWorker has completed, result: " + (int)a.Result); } );
bgw.ProgressChanged += ((s, a) => { ... });
bgw.DoWork += ((s, a) =>{
// Simulate some long running work
for (int i = 0; i < 10; i++)
{
Thread.Sleep(500);
// Report progress as percentage completed
bgw.ReportProgress(i * 10);
// Have we been cancelled?
bgw.DoWork += ((s, a) =>
{
// Simulate some long running work
for (int i = 0; i < 10; i++)
{
Thread.Sleep(500);
// Report progress as percentage completed
bgw.ReportProgress(i * 10);
// Have we been cancelled?
if (bgw.CancellationPending)
{
a.Cancel = true;
return;
}
} a.Result = 1234; // Return the result
});
// Now start execution
bgw.RunWorkerAsync();
基於Task的取消:
var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
try
{
int result = await Task.Factory.StartNew<int>(() =>
{
// Simulate some long running work
for (int i = 0; i < 10; i++)
{
Thread.Sleep(500);
// Have we been cancelled?
cancellationToken.ThrowIfCancellationRequested();
}
return 4321;
// Return the result
}, cancellationToken);
MessageBox.Show("BackgroundWorker has completed, result is: " + result);
}
catch (OperationCanceledException ex)
{
MessageBox.Show("Task BackgroundWorker was cancelled");
}