1. 程式人生 > 實用技巧 >.NET-.NET-指南-非同步程式設計模式:與其他非同步模式和型別互操作

.NET-.NET-指南-非同步程式設計模式:與其他非同步模式和型別互操作

ylbtech-Docs-.NET-.NET-指南-非同步程式設計模式:與其他非同步模式和型別互操作

1.返回頂部
1、

與其他非同步模式和型別互操作

.NET Framework 1.0 引進了IAsyncResult模式,也稱為Asynchronous Programming Model (APM)Begin/End模式。.NET Framework 2.0 增加了Event-based Asynchronous Pattern (EAP)從.NET Framework 4 開始,Task-based Asynchronous Pattern (TAP)

取代了 APM 和 EAP,但能夠輕鬆構建從早期模式中遷移的例程。

本主題內容:

任務和非同步程式設計模型 (APM)

從 APM 到 TAP

因為非同步程式設計模型 (APM)模式的結構非常合理,而且能夠輕鬆生成包裝,將 APM 實現公開為 TAP 實現。實際上,自 .NET Framework 4 之後的 .NET Framework 就包含採用FromAsync方法過載形式的幫助器例程來實現這種轉換。

請考慮Stream

類及其BeginReadEndRead方法,它們代表與同步Read方法對應的 APM:

C#
public int Read(byte[] buffer, int offset, int count)
C#
public IAsyncResult BeginRead(byte[] buffer, int offset,
                              int count, AsyncCallback callback,
                              object state)
C#
public int EndRead(IAsyncResult asyncResult)

可以使用TaskFactory<TResult>.FromAsync方法對此操作實現 TAP 包裝器,如下所示:

C#
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);
}

此實現類似於以下內容:

C#
 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;
}

從 TAP 到 APM

如果現有的基礎結構需要 APM 模式,則還需要採用 TAP 實現並在需要 APM 實現的地方使用它。由於任務可以組合,並且Task類實現IAsyncResult,您可以使用一個簡單的 helper 函式執行此操作。以下程式碼使用Task<TResult>類的擴充套件,但可以對非泛型任務使用幾乎相同的函式。

C#
public static IAsyncResult AsApm<T>(this Task<T> task,
                                    AsyncCallback callback,
                                    object state)
{
    if (task == null)
        throw new ArgumentNullException("task");

    var tcs = new TaskCompletionSource<T>(state);
    task.ContinueWith(t =>
                      {
                         if (t.IsFaulted)
                            tcs.TrySetException(t.Exception.InnerExceptions);
                         else if (t.IsCanceled)
                            tcs.TrySetCanceled();
                         else
                            tcs.TrySetResult(t.Result);

                         if (callback != null)
                            callback(tcs.Task);
                      }, TaskScheduler.Default);
    return tcs.Task;
}

現在,請考慮具有以下 TAP 實現的用例:

C#
public static Task<String> DownloadStringAsync(Uri url)

並且想要提供此 APM 實現:

C#
public IAsyncResult BeginDownloadString(Uri url,
                                        AsyncCallback callback,
                                        object state)
C#
public string EndDownloadString(IAsyncResult asyncResult)

以下示例演示了一種向 APM 遷移的方法:

C#
public IAsyncResult BeginDownloadString(Uri url,
                                        AsyncCallback callback,
                                        object state)
{
   return DownloadStringAsync(url).AsApm(callback, state);
}

public string EndDownloadString(IAsyncResult asyncResult)
{
   return ((Task<string>)asyncResult).Result;
}

任務和基於事件的非同步模式 (EAP)

包裝基於事件的非同步模式 (EAP)實現比包裝 APM 模式更為複雜,因為與 APM 模式相比,EAP 模式的變體更多,結構更少。為了演示,以下程式碼包裝了DownloadStringAsync方法。DownloadStringAsync接受 URI,在下載時引發DownloadProgressChanged事件,以報告進度的多個統計資訊,並在完成時引發DownloadStringCompleted事件。最終在指定 URI 中返回一個字串,其中包含頁面內容。

C#
 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;
}

任務和等待控制代碼

從等待控制代碼到 TAP

雖然等待控制代碼不能實現非同步模式,但高階開發人員可以在設定等待控制代碼時使用WaitHandle類和ThreadPool.RegisterWaitForSingleObject方法實現非同步通知。可以包裝RegisterWaitForSingleObject方法以在等待控制代碼中啟用針對任何同步等待的基於任務的替代方法:

C#
public static Task WaitOneAsync(this WaitHandle waitHandle)
{
    if (waitHandle == null)
        throw new ArgumentNullException("waitHandle");

    var tcs = new TaskCompletionSource<bool>();
    var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle,
        delegate { tcs.TrySetResult(true); }, null, -1, true);
    var t = tcs.Task;
    t.ContinueWith( (antecedent) => rwh.Unregister(null));
    return t;
}

使用此方法,可以在非同步方法中使用現有WaitHandle實現。例如,若要限制在任何特定時間執行的非同步運算元,可以利用訊號燈(System.Threading.SemaphoreSlim物件)。可以將併發執行的運算元目限制到N,方法為:初始化到N的訊號量的數目、在想要執行操作時等待訊號量,並在完成操作時釋放訊號量:

C#
static int N = 3;

static SemaphoreSlim m_throttle = new SemaphoreSlim(N, N);

static async Task DoOperation()
{
    await m_throttle.WaitAsync();
    // do work
    m_throttle.Release();
}

還可以構建不依賴等待控制代碼就完全可以處理任務的非同步訊號量。若要執行此操作,可以使用使用基於任務的非同步模式中所述的用於在Task

從 TAP 到等待控制代碼

正如前面所述,Task類實現IAsyncResult,且該實現公開IAsyncResult.AsyncWaitHandle屬性,該屬性會返回在Task完成時設定的等待控制代碼。可以獲得WaitHandleTask,如下所示:

C#
WaitHandle wh = ((IAsyncResult)task).AsyncWaitHandle;

另請參閱

2、
2.返回頂部
3.返回頂部
4.返回頂部
5.返回頂部
1、 https://docs.microsoft.com/zh-cn/dotnet/standard/asynchronous-programming-patterns/interop-with-other-asynchronous-patterns-and-types?view=netframework-4.7.2 2、
6.返回頂部
作者:ylbtech
出處:http://ylbtech.cnblogs.com/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。