如何将此代码转换为异步等待?

时间:2014-06-12 07:04:34

标签: c# task-parallel-library async-await

我有很多像这样的代码:

        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
}

2 个答案:

答案 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.FromAsyncAPM API包装到任务中。

不幸的是,没有非通用TaskCompletionSource会返回非通用Task因此,通常解决方法是使用Task<object>

答案 1 :(得分:0)

您需要更改DataFeed课程以支持该课程。你只需要在那里使用任务异步模式。这意味着DataFeed中的所有异步方法都必须返回Task(或某些Task<T>),并且它们应该被命名为ConnectAsync(例如)。

现在,使用Socket,这并不容易,因为XXXAsync上的Socket方法实际上并不值得等待!最简单的方法是单独使用TcpClientTcpListener(假设您使用的是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方法的包装器 - 非常方便。