我有很多像这样的代码:
var feed = new DataFeed(host, port);
feed.OnConnected += (conn) =>
{
feed.BeginLogin(user, pass);
};
feed.OnReady += (f) =>
{
//Now I'm ready to do stuff.
};
feed.BeginConnect();
正如您所看到的,我使用通常的异步操作方式。如何更改此代码以使用async await
?最好是这样的:
public async void InitConnection()
{
await feed.BeginConnect();
await feed.BeginLogin(user, pass);
//Now I'm ready
}
答案 0 :(得分:10)
您可以使用TaskCompletionSource<T>
将EAP(基于事件的异步模式)包装到Tasks中。目前尚不清楚如何在DataFeed类中处理错误和取消操作,因此您需要修改此代码并添加错误处理(sample):
private Task ConnectAsync(DataFeed feed)
{
var tcs = new TaskCompletionSource<object>();
feed.OnConnected += _ => tcs.TrySetResult(null);
feed.BeginConnect();
return tcs.Task;
}
private Task LoginAsync(DataFeed feed, string user, string password)
{
var tcs = new TaskCompletionSource<object>();
feed.OnReady += _ => tcs.TrySetResult(null);
feed.BeginLogin(user, pass);
return tcs.Task;
}
现在您可以使用以下方法:
public async void InitConnection()
{
var feed = new DataFeed(host, port);
await ConnectAsync(feed);
await LoadAsync(feed, user, pass);
//Now I'm ready
}
注意 - 您可以将这些异步方法移动到DataFeed类。但是,如果您可以修改DataFeed,那么最好使用TaskFactory.FromAsync
将APM API包装到任务中。
不幸的是,没有非通用TaskCompletionSource
会返回非通用Task
因此,通常解决方法是使用Task<object>
。
答案 1 :(得分:0)
您需要更改DataFeed
课程以支持该课程。你只需要在那里使用任务异步模式。这意味着DataFeed
中的所有异步方法都必须返回Task
(或某些Task<T>
),并且它们应该被命名为ConnectAsync
(例如)。
现在,使用Socket
,这并不容易,因为XXXAsync
上的Socket
方法实际上并不值得等待!最简单的方法是单独使用TcpClient
和TcpListener
(假设您使用的是TCP):
public async Task<bool> LoginAsync(TcpClient client, ...)
{
var stream = client.GetStream();
await stream.WriteAsync(...);
// Make sure you read all that you need, and ideally no more. This is where
// TCP can get very tricky...
var bytesRead = await stream.ReadAsync(buf, ...);
return CheckTheLoginResponse(buf);
}
然后从外面使用它:
await client.ConnectAsync(...);
if (!(await LoginAsync(client, ...))) throw new UnauthorizedException(...);
// We're logged in
这只是示例代码,我假设您实际上能够编写合适的TCP代码。如果你这样做,使用await
异步编写它并不是那么困难。只要确保你总是在等待一些异步I / O操作。
如果您只想使用Socket
执行相同操作,则可能必须使用Task.FromAsync
,它提供BeginXXX
/ EndXXX
方法的包装器 - 非常方便。