ylbtech-Docs-.NET-.NET-指南-异步编程模式:与其他异步模式和类型互操作 |
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:
public int Read(byte[] buffer, int offset, int count)
public IAsyncResult BeginRead(byte[] buffer, int offset,
int count, AsyncCallback callback,
object state)
public int EndRead(IAsyncResult asyncResult)
可以使用 TaskFactory<TResult>.FromAsync 方法对此操作实现 TAP 包装器,如下所示:
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);
}
此实现类似于以下内容:
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> 类的扩展,但可以对非泛型任务使用几乎相同的函数。
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 实现的用例:
public static Task<String> DownloadStringAsync(Uri url)
并且想要提供此 APM 实现:
public IAsyncResult BeginDownloadString(Uri url,
AsyncCallback callback,
object state)
public string EndDownloadString(IAsyncResult asyncResult)
以下示例演示了一种向 APM 迁移的方法:
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 中返回一个字符串,其中包含页面内容。
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 方法以在等待句柄中启用针对任何同步等待的基于任务的替代方法:
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的信号量的数目、在想要执行操作时等待信号量,并在完成操作时释放信号量:
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 ,如下所示:
WaitHandle wh = ((IAsyncResult)task).AsyncWaitHandle;
另请参阅
2.返回顶部 |
3.返回顶部 |
4.返回顶部 |
5.返回顶部 |
6.返回顶部 |
作者:ylbtech 出处:http://ylbtech.cnblogs.com/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 |