我们应该如何使用async await?

13 浏览
0 Comments

我们应该如何使用async await?

这个问题已经有答案了

为什么要使用async和return await,当你可以直接返回Task时?

如何使用和何时使用\"async\"和\"await\"

我正在看如何使用async await,但当我们有多个方法相互调用时,我不太清楚应该怎么做。我们应该总是使用await,还是只有在我们实际上要使用结果时才使用await?

例如,我们应该像这样做:

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

还是像这样做:

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

对于示例1,我们应该在每个方法中都使用await吗?

或者

对于示例2,我们只应该在最高层方法上使用await,当我们开始使用结果时,才使用await?

admin 更改状态以发布 2023年5月21日
0
0 Comments

两个选项都是合法的,每个选项都有自己更有效的情况。

当你想要处理异步方法的结果或在当前方法中处理可能的异常时,总是使用 await。

public async Task Execute()
{
    try
    {
        await RunAsync();
    }
    catch (Exception ex)
    {
        // Handle thrown exception
    }
}

如果在当前方法中不使用异步方法的结果-请返回任务。这种方法将延迟状态机的创建到调用者或最终任务将被等待的位置。如评论中所述,这可能会使执行效率略有提高。

但在某些情况下,即使你不使用结果并且不想处理可能的异常,你仍必须等待任务。

public Task GetEntity(int id)
{
    using (var context = _contextFactory.Create())
    {
        return context.Entities.FindAsync(id);
    }
}

在上述情况中,FindAsync可能返回未完成的任务,并且该任务将立即返回给调用者并且释放在using语句中创建的context对象。稍后,当调用者"等待"任务时,异常将被抛出,因为它将尝试使用已释放的对象(context)。

public async Task GetEntity(int id)
{
    using (var context = _contextFactory.Create())
    {
        return await context.Entities.FindAsync(id);
    }
}

在关于Async Await的惯例回答中,必须包括Stephen Cleary的博客链接
省略Async和Await

0
0 Comments

每次调用await时,它都会创建一块代码,捆绑变量,捕获同步上下文(如果适用),并创建一个继续执行到IAsyncStateMachine中的操作。基本上,在不使用async关键字返回一个Task将Give你一个小的运行时效率,并节省一堆CIL。请注意,.NET中的异步功能已经进行了许多优化。还请注意(并且很重要),在using语句中返回一个Task可能会引发一个AlreadyDisposedException异常。\n\n您可以在此处比较CIL和Plumbing差异:\n

\n因此,如果您的方法只是转发一个Task而不需要任何信息,则可以轻松地删除async关键字并直接返回Task。更多地是,在某些情况下,我们做的不仅仅是转发,还涉及分支。这就是Task.FromResult和Task.CompletedTask派上用场的地方,以帮助处理方法中可能出现的逻辑。即如果您想要给出结果(当时和那时),或者返回已完成的Task(分别)。最后,使用异步和等待模式处理异常时有微妙的区别。如果你返回一个Task,你可以使用Task.FromException来弹出返回的Task中的任何异常,就像async方法通常会执行的那样。\n\n无意义的例子\n\n

public Task 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(e); // Place exception on the task
   }
}

\n\n简而言之,如果你不太明白正在发生什么,只需await它;开销将是最小的。然而,如果你了解如何返回任务结果、完成的任务、将异常放置在任务上,或者只是转发等细节,你可以通过直接返回任务并绕过IAsyncStateMachine来节省一些CIL并为你的代码获得小的性能增益,省掉async关键字。\n\n


\n\n大约在这个时候,我会查找Stack Overflow用户和作者Stephen Cleary以及平行Stephen Toub。他们有大量关于异步和等待模式的博客和书籍,涵盖了所有的陷阱、编码礼仪和更多有趣的信息。

0