在单个异步方法中使用多个await有什么意义吗?

3
如果您有一个异步函数:
public async Task DoWork()
{
    await DoSomeWorkAsync();
    await DoSomeMoreWorkAsync();
}

现在,第一个await可以防止方法阻塞调用上下文,因此非常有用。
但是,在DoSomeWorkAsync完成后,该方法仍然在不同的上下文中运行,因为编译器已将其转换为类似于Task.ContinueWith(await DoSomeMoreWorkAsync())的代码。
所以,等待DoSomeMoreWorkAsync并异步运行它是否有任何目的?有没有任何缺点?如果存在DoSomeMoreWorkAsync的非异步版本,我应该使用它吗?
也就是说,这种做法有什么劣势:
public async Task DoWork()
{
    await DoSomeWorkAsync();
    DoSomeMoreWork();
}

编辑:这不是与单个方法中的多个等待相同的问题。那是在问会发生什么。我在问,这样做有没有任何优势。


@HimBromBeere:这与现有的问题不同。那个问题问发生了什么。我想知道是否有任何优势。在提问之前,我查看了那个问题。 - Yair Halberstadt
显然,问题在于您的异步方法应该执行多个步骤。对于调用成员,这些步骤是隐藏的,因为它不知道方法的内部情况。但是对于后者来说,这只是一个接着一个的流程。因此,对于异步方法,您有一些子任务应该是同步执行的,这意味着:首先执行 DoSometWork,当它完成时执行 DoSomeMoreWork。只有后者完成,由asny方法返回的整个任务才会完成。 - MakePeaceGreatAgain
2个回答

8
似乎你认为避免阻塞调用方是异步方法唯一的目的,但实际上并非如此。异步方法通常是异步的,因为它们在某个时刻执行异步IO操作,例如处理文件、数据库、Web请求等(或调用其他执行此操作的异步方法)。
当真正的异步IO正在进行时,应用程序中没有任何线程忙于等待其完成(虽然有一个线程,但它是整个应用程序的线程,而不是针对特定IO任务的线程)。
这意味着即使 await DoSomeMoreWorkAsync 在某些线程池线程上执行 - 当某个时刻到达异步IO时 - 此线程池线程将被释放,并可用于更有用的工作。
另一方面,如果您使用同步版本 (DoSomeMoreWork) - 它将在整个持续时间内阻塞当前线程池线程,包括IO,因此此线程将不可用于有用的工作。
在大量使用线程的应用程序(例如Web应用程序)中,尽可能释放线程可能非常重要。
除此之外,这个声明

But after DoSomeWorkAsync has completed, the method is anyways running in a different context

并不总是正确的。例如,在UI应用程序中,如果您从UI线程调用DoWork,则继续执行(await DoSomeMoreWorkAsync())也将在UI线程上执行。这意味着如果您将其替换为同步版本 - UI线程将在此期间冻结。

谢谢。这正是我所问的。 - Yair Halberstadt

4
那么,等待“DoSomeMoreWorkAsync”方法有什么用处吗?
如果是异步的,那么你绝对需要等待它,这样你的“DoWork”方法在完成“更多工作”之前不会结束。如果是异步的,并且您没有等待它,那么它实际上就是一个“fire and forget”,这很少是您想要的。
那么,运行它异步有什么用处吗?
这取决于函数。你不会将方法变为异步,只是因为你想把它们调用异步。方法是异步的,因为它们执行异步任务。
如果方法正在执行纯粹的CPU绑定工作,而且无论如何都不会异步运行,那么没有理由使其异步化。相反,将其保持同步以明确地告知调用者没有进行任何异步处理。
但如果它是某种受益于异步的东西,例如由于它执行了异步网络或I / O调用,则可能应该是异步的。然后你也应该等待它。
有什么缺点吗?
总是存在缺点。运行异步代码比运行同步代码更耗费资源,因为会产生和调用一定数量的开销(幸运的是我们编写异步代码时不必处理)。但是,在考虑真正异步代码可以给您带来的优势时,这种开销并不重要。
因此,你真的不应该使用这个来决定是否使某些东西异步化。仔细考虑方法的作用以及它是如何运行的,然后决定该过程是同步还是异步。

许多IO库都提供了异步和非异步版本的调用。因此,既然这种方法已经在异步运行,我应该使用非异步版本以避免开销吗? - Yair Halberstadt
2
这取决于函数的作用,而不是你想如何调用它。如果它是一个可以从异步中受益的进程,那么最好让它异步运行。而提供异步和同步调用的库通常只会在异步调用上阻塞以使其同步。 - poke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接