And*_*ens 1 c# task-parallel-library async-await
我必须使用一个 API 如下所示的库:
public void Connect();
...
public delegate void ConnectResultDelegate(bool succeeded, string msg);
public ConnectResultDelegate ConnectResultHandler;
Run Code Online (Sandbox Code Playgroud)
调用该Connect()方法后,ConnectResultHandler将调用回调委托。
API 公开了以类似“请求-响应”方式工作的其他方法;我猜委托的原因是这些方法与外部硬件设备交互,并且响应(委托调用)可能在很多毫秒内不会发生。
我希望我能以某种方式包装 API,让我能够以更“顺序”的方式使用它,更像是 async/await,大致如下:
void DoSomething()
{
_library.Connect();
// Wait for notification that this has completed
// Do something with the response passed to the delegate callback
_library.Configure(...);
// Wait for notification that this has completed
// Do something with the response
..etc..
}
Run Code Online (Sandbox Code Playgroud)
想法?重构库本身不是一个选择。
那里有一两个类似的 SO 问题,但它们的不同之处在于它们的委托被传递给方法,而不是单独的属性,这使得包装在任务中相对容易。
有很多答案展示了如何将事件或开始/结束异步操作转换为任务。但该代码不遵循任一模型的约定。它类似于基于事件的异步模型EAP,但不使用事件。如果您搜索事件到任务的转换,您会找到很多答案。不过,委托并不用于异步操作,因为 EAP 之前的约定是起诉异步编程模型 (APM)或Begin/End.
但处理过程仍然相同。它在与其他异步模式和类型的互操作中进行了描述。在所有情况下,TaskCompletionSource 用于创建在操作完成时发出信号的任务。
当类遵循 APM 约定时,可以使用TaskFactory.FromAsync方法将一对转换Beging/End为任务。在幕后FromAsync使用TaskCompletionSource返回一个在调用回调时发出信号的任务。互操作文档示例如下Stream.BeginRead:
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);
}
Run Code Online (Sandbox Code Playgroud)
使用委托与使用事件类似,互操作文章中也对此进行了介绍。适应这个问题,它看起来像这样:
public Task<bool> ConnectAsync(ThatService service)
{
if (service==null)
throw new ArgumentNullException(nameof(service));
var tcs=new TaskCompletionSource<bool>();
service.ConnectResultHandler=(ok,msg)=>
{
if(ok)
{
tcs.TrySetResult(true);
}
else
{
tcs.TrySetException(new Exception(msg));
}
};
return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)
ConnectAsync这将允许您在方法中使用async,例如:
public async Task MyMethod()
{
...
var ok=await ConnectAsync(_service);
...
}
Run Code Online (Sandbox Code Playgroud)
如果msg包含成功数据,您可以更改ConnectAsync为:
public Task<string> ConnectAsync(ThatService service)
{
if (service==null)
throw new ArgumentNullException(nameof(service));
var tcs=new TaskCompletionSource<string>();
service.ConnectResultHandler=(ok,msg)=>
{
if(ok)
{
tcs.TrySetResult(msg);
}
else
{
tcs.TrySetException(new Exception(msg));
}
};
return tcs.Task;
}
Run Code Online (Sandbox Code Playgroud)
您可以更改ConnectAsync为扩展方法,这将允许您像使用服务类的方法一样使用它:
public static class MyServiceExtensions
{
public static Task<string> ConnectAsync(this ThatService service)
{
//Same as before
}
}
Run Code Online (Sandbox Code Playgroud)
并使用它:
public async Task MyMethod()
{
...
var msg=await _service.ConnectAsync();
...
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1518 次 |
| 最近记录: |