開篇第一句:.net4.5中非同步方法呼叫的實現遠不如看起來的那麼簡單,編譯器背後代替開發人員產生了大量的代碼 做了好多事情使它看起來簡單了,這些代碼和過去實現非同步作業時必須開發人員手動編寫並維護的樣板代碼的數量類似。此外,編譯器產生的程式碼會在 .NET Framework 中調用庫代碼,再次代替開發人員完成更多的工作。要獲得正確的思維模式並使用這一模式做出合適的開發決策,重要的一點是瞭解編譯器代替您產生了哪些內容。
.net4.5中定義了大量的非同步方法呼叫***Async(例如Windows.System.Launcher.LaunchUriAsync() )這些方法的傳回型別是IAsyncInfo或者繼承自該介面的IAsyncAction、IAsyncActionWithProgress、IAsyncOperation 和 IAsyncOperationWithProgress等(注意在Windows 8 Consumer Preview版本中這些方法都改為暖開機的,非同步方法呼叫會在將操作返回到調用程式之前啟動操作,所以無需***Async.Start()等操作)。
WinRT 非同步模型的核心介面依託於 IAsyncInfo 而構建。該核心介面可以定義非同步作業(例如,目前狀態、取消操作的功能和失敗操作的錯誤等)的屬性。使用者可以通過該類型的傳回值得到非同步方法呼叫***Async的目前狀態Status,標識Id,以及該非同步方法呼叫失敗的錯誤資訊ErrorCode,並且可以取消和關閉該操作。
View Code
public interface IAsyncInfo { // 摘要: // Gets a string that describes an error condition of the asynchronous operation. // // 返回結果: // The error string. Exception ErrorCode { get; } // // 摘要: // Gets the handle of the asynchronous operation. // // 返回結果: // The handle. uint Id { get; } // // 摘要: // Gets a value that indicates the status of the asynchronous operation. // // 返回結果: // The status of the operation. AsyncStatus Status { get; } // 摘要: // Cancels the asynchronous operation. void Cancel(); // // 摘要: // Closes the asynchronous operation. void Close(); }
但該特定介面缺少對非同步作業來說無疑是至關重要的功能:當操作完成時,通過回調通知監聽器以及非同步作業運行時進度報告。該功能有意地劃分到了四個依賴 IAsyncInfo的其他介面中,而 WinRT 中的每個非同步作業都需要實施以下四個介面之一。
public interface IAsyncAction : IAsyncInfo{ AsyncActionCompletedHandler Completed { get; set; } void GetResults();}public interface IAsyncOperation<TResult> : IAsyncInfo{ AsyncOperationCompletedHandler<TResult> Completed { get; set; } TResult GetResults();}public interface IAsyncActionWithProgress<TProgress> : IAsyncInfo{ AsyncActionWithProgressCompletedHandler<TProgress> Completed { get; set; }
AsyncActionProgressHandler<TProgress> Progress { get; set; } void GetResults();}public interface IAsyncOperationWithProgress<TResult, TProgress> : IAsyncInfo{ AsyncOperationWithProgressCompletedHandler<TResult, TProgress> Completed { get; set; }
AsyncOperationProgressHandler<TResult, TProgress> Progress { get; set; } TResult GetResults();}
例如我們可以這麼使用:(更多資訊請參考http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/03/26/windows.aspx)
private void btnDoWork_Click(object sender, RoutedEventArgs e){ int result = 0;
var op = ThreadPool.RunAsync(delegate { result = Compute(); })
op.Completed = delegate(IAsyncAction asyncAction, AsyncStatus asyncStatus)
{
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, delegate
{
switch (asyncStatus)
{
case AsyncStatus.Completed:
btnDoWork.Content = result.ToString();
break;
case AsyncStatus.Error:
btnDoWork.Content = asyncAction.ErrorCode.Message;
break;
case AsyncStatus.Canceled:
btnDoWork.Content = "A task was canceled";
break;
}
});
};}
但為了處理一個非同步呼叫,我們就需要編寫大量代碼,手動處理完成回調,手動封送回 UI 線程,明確檢查完成狀態等等。於是一個關鍵字await應運而生。有了await我們可以這麼寫:
private async void btnDoWork_Click(object sender, RoutedEventArgs e)
{
try
{
int result = 0;
await ThreadPool.RunAsync(delegate { result = Compute(); }); // 該非同步作業中產生的異常會被傳播到當前線程中並在當前線程中得到處理。
btnDoWork.Content = result.ToString();
}
catch (Exception exc) { btnDoWork.Content = exc.Message; }
}
注意async關鍵字,使用 async 關鍵字標記方法,會導致 C# 或 Visual Basic 編譯器使用狀態機器重新編寫該方法的實施。藉助此狀態機器,編譯器可以在該方法中插入多個中斷點,以便該方法可以在不阻止線程的情況下,掛起和恢複其執行。這些中斷點不會隨意地插入。它們只會在您明確使用 await 關鍵字的位置插入. 詳細資料請參考:http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/04/30/winrt-await.aspx
總結一下:
async關鍵字的作用:
1.告訴編譯器你想在該方法中使用await關鍵字(只可以在標有async關鍵字的方法或者lambda運算式中使用await關鍵字),如果標有async方法中沒有await,編譯器編譯時間會發出警告。而編譯器碰到使用aysnc標記的方法會使用狀態機器重新編寫該方法的實施。這樣該方法在await關鍵字出現的地方可以在不阻止線程的情況下,掛起和恢複其執行。
2.告訴編譯器來自該操作的任何異常將得到傳播或作為結果返回(通過 GetResult),如果該方法傳回值是Task或者Task<TResult>,就意味著任何傳回值或者該方法中未處理的異常都會儲存到返回的Task中;對於一個傳回值是Void的方法來說,這意味著 任何異常都會傳播到調用者的上下文中. 也就是上邊例子中所展示的。
await關鍵字的作用:
告訴編譯器在標記async的方法中插入一個掛起/恢複執行的點。
Task Task〈TResult>和WinRT非同步方法呼叫之間的互換:
在構建 WinRT 庫時,該庫中所有全域公開的非同步作業都會強型別化為返回這四個介面之一。與此相對,從 .NET 庫公開的新非同步作業遵循基於任務的非同步模式 (TAP)。對於不返回結果的操作返回 Task,而對於返回結果的操作則返回 Task<TResult>。Task 和 Task<TResult> 不會實施這些 WinRT 介面,通用語言執行平台 (CLR) 也不會暗中掩飾它們的差異(對於某些類型會如此,例如 WinRT Windows.Foundation.Uri 類型和 BCL System.Uri類型)。而是我們需要明確地從一種模式轉換到另一種。Task和Task<>也可以使用AsAsyncAction, AsAsyncOperation<>轉化為IAsyncAction、IAsyncActionWithProgress、IAsyncOperation 和 IAsyncOperationWithProgress等介面方法,後台實現機製為適配器模式。
Task<int> t;
t.AsAsyncAction();
t.AsAsyncOperation<int>
另外這些非同步方法呼叫還可以通過AsTask和AsTask<>等方法轉化成為Task或者Task<>,後台使用擴充方法實現,類似於Linq的實現方式。
IAsyncOperationWithProcess<int, string> op;
op.AsTask<int, string>
詳細瞭解請參考以下連結。
Reference:
http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/03/26/windows.aspx
http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/04/30/winrt-await.aspx
http://blogs.msdn.com/b/windowsappdev_cn/archive/2012/06/22/net-winrt.aspx
http://blogs.msdn.com/b/pfxteam/archive/2012/04/12/10293335.aspx
http://www.microsoft.com/en-us/download/details.aspx?id=19957
http://msdn.microsoft.com/zh-CN/async
關於TAP
在對比了早期IAsyncResult模式也叫APM模式和事件架構非同步模式Event-based Asynchronous PatternEAP,基於任務的非同步模式Task-based Asynchronous Pattern 有很大的優勢。對比一下代碼:
public class MyClass{ public int Read(byte [] buffer, int offset, int count);} The APM counterpart to this method would expose the following two methods:public class MyClass{ public IAsyncResult BeginRead( byte [] buffer, int offset, int count, AsyncCallback callback, object state); public int EndRead(IAsyncResult asyncResult);}The EAP counterpart would expose the following set of types and members:public class MyClass{ public void ReadAsync(byte [] buffer, int offset, int count); public event ReadCompletedEventHandler ReadCompleted;}public delegate void ReadCompletedEventHandler( object sender, ReadCompletedEventArgs eventArgs);public class ReadCompletedEventArgs : AsyncCompletedEventArgs{ public int Result { get; }}The TAP counterpart would expose the following single method:public class MyClass{ public Task<int> ReadAsync(byte [] buffer, int offset, int count);}
詳細參考:http://www.microsoft.com/en-us/download/details.aspx?id=19957
另外推薦兩個很好的post,關於.net4中引入的非同步編程工作平行程式庫(Task Parallel Library)以及CLR4中線程池的內部實現。
http://www.codeproject.com/Articles/152765/Task-Parallel-Library-1-of-n
http://www.danielmoth.com/Blog/New-And-Improved-CLR-4-Thread-Pool-Engine.aspx