我们应该如何使用异步等待?

Vin*_*ent 40 c# asynchronous async-await

我一直在研究如何使用异步等待,但是当我们有多个互相调用的方法时,我不太了解它。我们应该始终使用等待还是仅在实际准备好使用结果时才使用等待?

因此,例如,我们应该这样做吗:

async Task<string[]> FooAsync()
{
    var info = await Func1();
    return info.split('.');
}

async Task<string> Func1()
{
    return await Func2();
}

async Task<string> Func2()
{
    return await tcpClient.ReadStringAsync();
}
Run Code Online (Sandbox Code Playgroud)

或像这样:

async Task<string[]> FooAsync()
{
    var info = await Func1();
    return info.split('.');
}

Task<string> Func1()
{
    return Func2();
}

Task<string> Func2()
{
    return tcpClient.ReadStringAsync();
}
Run Code Online (Sandbox Code Playgroud)

根据示例1,我们应该在每种方法中始终使用await吗?
还是
按照示例2,当我们开始使用结果时,我们应该只在最上面的方法上使用await吗?

AAA*_*ddd 28

每次您调用await它时,都会创建大量代码来捆绑变量,捕获同步上下文(如果适用)并在中创建延续IAsyncStateMachine

从本质上讲,返回Task不带async 关键字的 a将给您带来很小的运行时效率,并节省了许多CIL。请注意,.NET中的Async功能也已经进行了许多优化。还要注意(并且重要的是)在语句中返回a 可能会引发Already Disposed ExceptionTaskusing

您可以在此处比较CIL和管道的差异

因此,如果您的方法只是转发a Task而不想要任何东西,则可以轻松地删除async关键字并Task直接返回。

而且,有时候我们要做的不只是转发,还涉及分支。这是在哪里,Task.FromResultTask.CompletedTask开始发挥作用,协助处理的方法可能会出现什么逻辑。即,如果您要给出一个结果(然后然后),或分别返回一个Task完成的结果

最后,异步和等待模式在处理Exception时有细微的差别。如果您要传回Task,则可以像通常方法一样使用Task.FromException<T>,在传回的讯息上弹出任何例外。Taskasync

荒谬的例子

public Task<int> DoSomethingAsync(int someValue)
{
   try
   {
      if (someValue == 1)
         return Task.FromResult(3); // Return a completed task

      return MyAsyncMethod(); // Return a task
   }
   catch (Exception e)
   {
      return Task.FromException<int>(e); // Place exception on the task
   }
}
Run Code Online (Sandbox Code Playgroud)

简而言之,如果您不太了解正在发生的事情,await那就去吧。开销将最小。但是,如果您了解如何返回任务结果完成的任务,在任务上放置异常或只是转发的字幕。通过删除直接返回任务并绕过关键字的关键字,可以节省一些CIL,并使代码的性能有所提高。asyncIAsyncStateMachine


大约在这个时候,我将查找Stack Overflow用户和作者Stephen Cleary以及Parallel Stephen Toub先生。他们有大量的博客和书籍,专门讨论异步和等待模式,所有陷阱,编码礼节以及许多您肯定会感兴趣的信息。


Fab*_*bio 12

这两个选项都是合法的,每个选项都有自己的场景,在这种情况下,它比另一个更有效。

当然,当您要处理异步方法的结果或处理当前方法中可能的异常时,请始终使用await

public async Task Execute()
{
    try
    {
        await RunAsync();
    }
    catch (Exception ex)
    {
        // Handle thrown exception
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您不在当前方法中使用异步方法的结果,请返回Task。这种方法将延迟状态机的创建到调用者或将要等待最终任务的地方。正如评论中指出的那样,可以使执行更有效率。

但是在某些情况下,您必须等待任务,即使您对结果什么也不做,也不想处理可能的异常

public Task<Entity> GetEntity(int id)
{
    using (var context = _contextFactory.Create())
    {
        return context.Entities.FindAsync(id);
    }
}
Run Code Online (Sandbox Code Playgroud)

在上述情况下,FindAsync可以返回未完成的任务,并且此任务将立即返回给调用方并处理contextusing语句中创建的对象。
稍后当调用者将“等待”任务时,将抛出异常,因为它将尝试使用已处置的object(context)。

public async Task<Entity> GetEntity(int id)
{
    using (var context = _contextFactory.Create())
    {
        return await context.Entities.FindAsync(id);
    }
}
Run Code Online (Sandbox Code Playgroud)

传统上,有关Async Await的答案必须包含指向Stephen Cleary的博客Eliding Async and Await的链接