我们应该如何使用async await?
我们应该如何使用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?
两个选项都是合法的,每个选项都有自己更有效的情况。
当你想要处理异步方法的结果或在当前方法中处理可能的异常时,总是使用 await。
public async Task Execute() { try { await RunAsync(); } catch (Exception ex) { // Handle thrown exception } }
如果在当前方法中不使用异步方法的结果-请返回任务。这种方法将延迟状态机的创建到调用者或最终任务将被等待的位置。如评论中所述,这可能会使执行效率略有提高。
但在某些情况下,即使你不使用结果并且不想处理可能的异常,你仍必须等待任务。
public TaskGetEntity(int id) { using (var context = _contextFactory.Create()) { return context.Entities.FindAsync(id); } }
在上述情况中,FindAsync可能返回未完成的任务,并且该任务将立即返回给调用者并且释放在using语句中创建的context对象。稍后,当调用者"等待"任务时,异常将被抛出,因为它将尝试使用已释放的对象(context)。
public async TaskGetEntity(int id) { using (var context = _contextFactory.Create()) { return await context.Entities.FindAsync(id); } }
在关于Async Await的惯例回答中,必须包括Stephen Cleary的博客链接
省略Async和Await
每次调用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 TaskDoSomethingAsync(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。他们有大量关于异步和等待模式的博客和书籍,涵盖了所有的陷阱、编码礼仪和更多有趣的信息。