为什么在必须使用await时使用异步?

Tob*_*oby 26 .net c# asynchronous async-await

我一直坚持这个问题,并没有真正找到任何有用的澄清,为什么会这样.

如果我有一个async像这样的方法:

public async Task<bool> MyMethod()
{
    // Some logic
    return true;
}

public async void MyMethod2()
{
    var status = MyMethod(); // Visual studio green lines this and recommends using await
}
Run Code Online (Sandbox Code Playgroud)

如果我await在这里使用,那么异步方法有什么意义呢?asyncVS告诉我打电话不是没用await吗?这不会破坏将任务卸载到线程而不等待它完成的目的吗?

Ser*_*kiy 24

如果我在这里使用await,那么异步方法有什么意义呢?

await不阻止线程.MyMethod2将同步运行,直到达到await表达式.然后MyMethod2暂停,直到等待的任务(MyMethod)完成.虽然MyMethod没有完成控制将返回给调用者MyMethod2.这就是await- 调用者将继续做它的工作.

难道它不会让VS告诉我等待的异步无用吗?

async 只是一个标志,意思是'在方法的某个地方你有一个或多个等待'.

这不会破坏将任务卸载到线程而不等待它完成的目的吗?

如上所述,您不必等待任务完成.这里什么都没有阻止.

注意:要遵循框架命名标准,我建议您Async为异步方法名称添加后缀.

  • 不建议使用 `async void` _除非_它用于事件处理程序。 (2认同)

Lua*_*aan 19

这不会破坏将任务卸载到线程而不等待它完成的目的吗?

当然是.但这不是await/async的目的.目的是允许您编写使用异步操作而不浪费线程的同步代码,或者更一般地说,允许调用者对或多或少的异步操作进行控制.

基本思想是,只要你正确使用await和async,整个操作就会显得同步.这通常是件好事,因为你做的大多数事情都是同步的 - 比如说,你不想在请求用户名之前创建用户.所以你会做这样的事情:

var name = await GetNameAsync();
var user = await RemoteService.CreateUserAsync(name);
Run Code Online (Sandbox Code Playgroud)

这两个操作相互同步; 第二个不会(并且不能!)在第一个之前发生.但它们(不一定)与其来电者同步.一个典型的例子是Windows窗体应用程序.想象一下,你有一个按钮,点击处理程序包含上面的代码 - 所有代码都在UI线程上运行,但同时,在你运行时,awaitUI线程可以自由地执行其他任务(类似于使用Application.DoEvents直到操作完成).

同步代码更易于编写和理解,因此这使您可以获得异步操作的大部分好处,而无需使代码更难理解.而且你不会失去异步做事的能力,因为Task它本身就是一个承诺,而你并不总是立即await去做.想象一下,这GetNameAsync需要花费很多时间,但与此同时,在完成之前,您需要完成一些CPU工作:

var nameTask = GetNameAsync();

for (int i = 0; i < 100; i++) Thread.Sleep(100); // Important busy-work!

var name = await nameTask;
var user = await RemoteService.CreateUserAsync(name);
Run Code Online (Sandbox Code Playgroud)

现在你的代码仍然是美妙的同步 - await是同步点 - 而你可以与异步操作并行完成其他事情.另一个典型示例是并行触发多个异步请求,但保持代码与所有请求的完成同步:

var tasks = urls.Select(i => httpClient.GetAsync(i)).ToArray();

await Task.WhenAll(tasks);
Run Code Online (Sandbox Code Playgroud)

这些任务是相互异步的,但不是他们的调用者,这仍然是美妙的同步.

我已经制作了一个(不完整的)网络样本,它以这种方式使用await.基本的想法是,虽然大多数代码在逻辑上是同步的(有一个协议要遵循 - 请求登录 - >验证登录 - >读取循环......;你甚至可以看到并行等待多个任务的部分) ,当你实际有CPU工作时,你只使用一个线程.Await使得这几乎是微不足道的 - 使用continuation或旧的Begin/End异步模型做同样的事情将会更加痛苦,特别是在错误处理方面.Await让它看起来很干净.

  • "你不必总是马上等待它" - 我认为这是解释的核心.不幸的是,我们看到的几乎所有代码示例都会立即调用等待,从而导致这种混乱.您的nameTask示例很好地说明了这个概念. (3认同)
  • @JohanMaes 当然,`await` 的要点仍然是它允许您以有效的方式放弃 CPU 时间。您可能不想使用 async 方法的唯一真实情况是,如果它最后只有一个 `await`(因此您可以只返回结果),即使这样也需要考虑,因为调试变得有点不同。如果您在一个序列中有多个操作,`await` 仍然允许您以简单的方式链接它们(而不是必须处理延续)。 (2认同)

Sef*_*efe 5

异步方法不会在其他线程上自动执行.实际上,情况恰恰相反:一个async方法总是在调用线程中执行.async意味着这是一个可以产生异步操作的方法.这意味着它可以在等待其他执行完成时将控制权返回给调用者.因此,asnync方法是一种等待其他异步操作的方法.

既然你什么都不做,等待中MyMethod2,async是没有意义在这里,所以你的编译器会发出警告.

有趣的是,实现async方法的团队已经承认标记方法async并不是必需的,因为只需await在方法体中使用,编译器就可以将其识别为异步.async添加了使用关键字的要求,以避免对await用作变量名的现有代码进行更改.