async和await是仅用于基于GUI的异步编程吗?

38

我一直在阅读关于C#中新的asyncawait运算符的内容,并尝试弄清楚它们可能对我有什么用处。我研究了几篇MSDN文章,以下是我读懂的内容:

你可以在Windows Forms和WPF事件处理程序中使用async,这样它们就能够在执行大部分操作时不会阻塞UI线程,从而执行较长时间的任务。

async void button1_Click(object sender, EventArgs e)
{
    // even though this call takes a while, the UI thread will not block
    // while it is executing, therefore allowing further event handlers to
    // be invoked.
    await SomeLengthyOperationAsync();
}
使用await的方法必须是async的,这意味着你代码中任何位置使用了async函数,都会使从UI事件处理程序到最低级async方法的所有调用序列中的方法也成为async的。
换句话说,如果您使用普通的老式ThreadStart入口点创建线程(或使用老式的static int Main(string[] args)控制台应用程序),则不能使用asyncawait,因为在某个时刻您必须使用await,并使使用它的方法成为async,因此在调用方法中,您还必须使用await并使其成为async,以此类推。但是一旦到达线程入口点(或Main()),就没有调用者可以将await控制权交出去。
因此,如果没有使用标准的WinForms和WPF消息循环的GUI,您基本上无法使用asyncawait。我想这一切确实有道理,因为MSDN指出async编程并不意味着多线程,而是利用UI线程的空闲时间。当使用控制台应用程序或具有用户定义入口点的线程时,执行异步操作需要使用多线程(如果不使用兼容的消息循环)。
我的问题是,这些假设准确吗?

3
即使完全误解了文档,也应该为一个好问题点赞。+1 - Jon Skeet
在这里查看一些非 GUI 的示例:http://msdn.microsoft.com/zh-cn/library/hh191443.aspx - Zaid Masud
4个回答

30
基本上,你不能在没有使用标准的WinForms和WPF消息循环的GUI的情况下使用async和await。
绝对不是这样的。在Windows Forms和WPF中,async/await有一个方便的属性,即当您等待的异步操作完成时,会返回到UI线程,但这并不意味着这就是它的唯一目的。
如果异步方法在线程池线程上执行(例如在Web服务中),那么延续(异步方法的其余部分)将简单地在任何线程池线程中执行,并且上下文(安全性等)将相应地保留。这仍然非常有用,可以保持线程数量较少。
例如,假设您有一个高流量的Web服务,它大多数时间都将请求代理到其他Web服务。它花费大部分时间在等待其他事情,无论是由于网络流量还是由于真正的另一个服务(例如数据库)。您不应该需要大量线程来处理这个问题-但是使用阻塞调用,您自然而然地会为每个请求结束一个线程。使用async / await,您会得到很少的线程,因为即使有许多请求“正在飞行”,实际上很少有请求需要在任何一个时间点执行任何工作。
麻烦在于,async/await最容易通过UI代码进行演示,因为每个人都知道正确使用后台线程或在UI线程中执行过多的工作的痛苦。但这并不意味着它是该功能有用的唯一位置-远非如此。
各种服务器端技术(例如MVC和WCF)已经支持异步方法,我希望其他技术也会效仿。

2
现在我已经阅读了所有答案,我认为我知道我错在哪里了。我做对的是基本上是常规的回调式编程,但代码更好了。我假设await魔法使用某些严格与GUI相关的东西来知道在哪里调用回调(即await之后方法的其余部分),而实际上它使用SynchronizationContext.CurrentTaskScheduler.Current和“任何操作系统认为合适”的东西,例如线程池,在这个顺序中优先级排名。不幸的是,我是那种不舒服使用我自己无法编程的东西的人。 - dialer
1
@dialer:希望窥探魔法背后的东西没有错。您可能会喜欢我的Eduasync博客系列:http://msmvps.com/blogs/jon_skeet/archive/tags/Eduasync/default.aspx - Jon Skeet
Eduasync 系列的起始链接已更新(之前的链接无法使用):http://blogs.msmvps.com/jonskeet/2011/05/08/eduasync-part-1-introduction/ - David Dowdle

8
使用await的方法必须是异步的,这意味着您在代码中任何地方使用任何异步函数都会强制从UI事件处理程序到最低级别异步方法的所有调用序列中的所有方法也是异步的。
事实并非如此-标记为async的方法只意味着它们可以使用await,但调用这些方法的方法没有限制。如果该方法返回Task或Task,则它们可以使用ContinueWith或4.0中可以对任务执行的任何其他操作。
一个很好的非UI示例是MVC4 AsyncController。
最终,async/await主要是关于让编译器重写,以便您可以编写看起来像同步代码的代码,并避免在添加async/await之前必须执行的所有回调。它还有助于SynchronizationContext处理,对于具有线程亲和性的场景(如UI框架、ASP.NET)非常有用,但即使没有这些,它仍然很有用。例如,Main始终可以执行DoStuffAsync().Wait();。 :)

7

我的问题是,这些假设准确吗?

不准确。

你可以在Windows Forms和WPF事件处理程序中使用async,以便它们可以执行漫长的任务而不会阻塞UI线程,同时执行操作的大部分内容。

正确。对于其他UI应用程序,包括Silverlight和Windows Store,也是如此。

并且对于ASP.NET也是真的。在这种情况下,没有阻止HTTP请求线程。

使用await的方法必须是异步的,这意味着您代码中任何地方使用的任何异步函数最终都会强制调用序列中从UI事件处理程序到最低级异步方法的所有方法都是异步的。

这是一种最佳实践("async all the way down"),但不是绝对必要的。您可以阻塞异步操作的结果;许多人选择在控制台应用程序中这样做。

一个普通的老式ThreadStart入口点

嗯...我必须对"ordinary good old"提出异议。正如我在博客中解释的那样,Thread是进行后台操作的最糟糕的选择之一

我建议您查看我的asyncawait入门介绍,然后再查看async / await FAQ


0
  1. async-await只是Task类操作的包装器,它是所谓的Tasks Parallel Library - TPL的一部分(在async-await自动生成代码技术之前发布)。
  2. 因此,事实上,在async-await中不能使用任何对UI控件的引用。
  3. 通常,async-await是任何Web和服务器关系、加载资源、SQL的强大工具。它可以智能等待数据并保持UI活动。
  4. 通常TPL应用程序:从简单的大型循环到基于共享数据的复杂计算中的多阶段并行计算(ContinueWith等)。

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