.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 类及其 BeginRead 和 EndRead 方法,它们代表与同步 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 完成时设置的等待句柄。 可以获得 WaitHandle 的 Task ,如下所示:

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

另请参阅

2、
2.返回顶部
 
3.返回顶部
 
4.返回顶部
 
5.返回顶部
1、
2、
 
6.返回顶部
 
.NET-.NET-指南-异步编程模式:与其他异步模式和类型互操作 作者:ylbtech
出处:http://ylbtech.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

.NET-.NET-指南-异步编程模式:与其他异步模式和类型互操作

上一篇:jquery 获取元素


下一篇:SQL Server 开启DTC分布式事务