如何以及何时使用`async`和`await`

从我的理解, asyncawait的主要事情之一是使代码易于编写和读取 – 但使用它们相当于产卵后台线程来执行长期的逻辑?

我目前正在尝试最基本的例子。 我在内部添加了一些评论。 你能为我澄清吗?

 // I don't understand why this method must be marked as `async`. private async void button1_Click(object sender, EventArgs e) { Task<int> access = DoSomethingAsync(); // task independent stuff here // this line is reached after the 5 seconds sleep from // DoSomethingAsync() method. Shouldn't it be reached immediately? int a = 1; // from my understanding the waiting should be done here. int x = await access; } async Task<int> DoSomethingAsync() { // is this executed on a background thread? System.Threading.Thread.Sleep(5000); return 1; } 

当使用asyncawait编译器在后台生成一个状态机。

下面是一个例子,我希望我能解释一些正在进行的高级细节:

 public async Task MyMethodAsync() { Task<int> longRunningTask = LongRunningOperationAsync(); // independent work which doesn't need the result of LongRunningOperationAsync can be done here //and now we call await on the task int result = await longRunningTask; //use the result Console.WriteLine(result); } public async Task<int> LongRunningOperationAsync() // assume we return an int from this long running operation { await Task.Delay(1000); // 1 second delay return 1; } 

好的,这里发生了什么事情:

  1. Task<int> longRunningTask = LongRunningOperationAsync(); 开始执行LongRunningOperation

  2. 让我们假设主线程(线程ID = 1)完成独立工作,然后await longRunningTask达到。

    现在,如果longRunningTask尚未完成并且仍在运行,则MyMethodAsync()将返回其调用方法,因此主线程不会被阻塞。 当longRunningTask完成时,ThreadPool(可以是任何线程)的一个线程将在其上一个上下文中返回MyMethodAsync()并继续执行(在这种情况下将结果打印到控制台)。

第二种情况是longRunningTask已经完成执行,结果是可用的。 当到达await longRunningTask我们已经有了结果,所以代码将继续在同一个线程上执行。 (在这种情况下打印结果到控制台)。 当然这不是上述例子的情况,其中涉及Task.Delay(1000)

进一步的其他答案,看看等待(C#参考)

更具体地说就是包含的例子,它解释了你的情况

以下Windows窗体示例说明了如何在asynchronous方法WaitAsynchronouslyAsync中使用await。 将该方法的行为与WaitSynchronously的行为进行对比。 如果没有等待运算符应用于某个任务,则尽pipe在其定义中使用了asynchronous修饰符并且在其正文中调用了Thread.Sleep,但WaitSynchronously将同步运行。

 private async void button1_Click(object sender, EventArgs e) { // Call the method that runs asynchronously. string result = await WaitAsynchronouslyAsync(); // Call the method that runs synchronously. //string result = await WaitSynchronously (); // Display the result. textBox1.Text += result; } // The following method runs asynchronously. The UI thread is not // blocked during the delay. You can move or resize the Form1 window // while Task.Delay is running. public async Task<string> WaitAsynchronouslyAsync() { await Task.Delay(10000); return "Finished"; } // The following method runs synchronously, despite the use of async. // You cannot move or resize the Form1 window while Thread.Sleep // is running because the UI thread is blocked. public async Task<string> WaitSynchronously() { // Add a using directive for System.Threading. Thread.Sleep(10000); return "Finished"; } 

从我的理解,asynchronous和等待的主要事情之一是使代码易于编写和阅读。

他们是让asynchronous代码易于编写和读取,是的。

与产卵后台线程执行长时间逻辑是一样的吗?

一点也不。

//我不明白为什么这个方法必须标记为“async”。

async关键字启用了await关键字。 所以任何使用await方法都必须标记为async

//在DoSomethingAsync()方法的5秒钟睡眠后,达到此行。 不应该立即达到?

不,因为async方法默认情况下不在另一个线程上运行。

//是否在后台线程上执行?

没有。


你可能会发现我的async / await介绍有帮助。 官方MSDN文档也非常好(特别是TAP部分), async团队发布了一个很好的FAQ 。

在一个简单的控制台程序中显示上述解释 –

 class Program { static void Main(string[] args) { TestAsyncAwaitMethods(); Console.WriteLine("Press any key to exit..."); Console.ReadLine(); } public async static void TestAsyncAwaitMethods() { await LongRunningMethod(); } public static async Task<int> LongRunningMethod() { Console.WriteLine("Starting Long Running method..."); await Task.Delay(5000); Console.WriteLine("End Long Running method..."); return 1; } } 

输出是:

 Starting Long Running method... Press any key to exit... End Long Running method... 

从而,

  1. Main通过TestAsyncAwaitMethods启动长时间运行的方法。 立即返回而不停止当前的线程,我们立即看到“按任意键退出”消息
  2. 所有这一切,LongRunningMethod运行在后台。 一旦完成,线程池中的另一个线程将获取这个上下文并显示最终的消息

因此,没有线程被阻塞。

我一直在努力寻找asynchronous/等待实际的简单例子。 一旦我终于明白了,我创build了这个简单的小例子,我认为它提供了一个非常好的总体思路,以便在高级别使用async / await时发生了什么。

注意: Task.Delay(1000)只会阻塞1秒

 Console.WriteLine(DateTime.Now); // This block takes 1 second to run because all // 5 tasks are running simultaneously { var a = Task.Delay(1000); var b = Task.Delay(1000); var c = Task.Delay(1000); var d = Task.Delay(1000); var e = Task.Delay(1000); await a; await b; await c; await d; await e; } Console.WriteLine(DateTime.Now); // This block takes 5 seconds to run because each "await" // pauses the program until the task finishes { await Task.Delay(1000); await Task.Delay(1000); await Task.Delay(1000); await Task.Delay(1000); await Task.Delay(1000); } Console.WriteLine(DateTime.Now); 

OUTPUT:

 5/24/2017 2:22:50 PM 5/24/2017 2:22:51 PM (First block took 1 second) 5/24/2017 2:22:56 PM (Second block took 5 seconds) 

我想你已经select了System.Threading.Thread.Sleep一个坏例子

一个async任务点是让它在后台执行而不locking主线程,比如做一个DownloadFileAsync

System.Threading.Thread.Sleep不是“正在完成”,它只是睡觉,因此你的下一行是5秒后到达…

阅读这篇文章,我认为这是一个很好的解释asyncawait概念: http : //msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx

下面是一个快速的控制台程序,以便对那些关注的人进行清楚的说明 “TaskToDo”方法是您想要进行asynchronous的长时间运行的方法。 使其运行asynchronous由TestAsync方法完成。 testing循环方法只是运行“TaskToDo”任务,并运行它们的asynchronous。 您可以在结果中看到,因为它们没有按照从运行到运行的相同顺序完成 – 它们在完成时向控制台UI线程报告。 简单化,但我认为简单的例子比其他更多的例子更好地展现了这个模式的核心:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace TestingAsync { class Program { static void Main(string[] args) { TestLoops(); Console.Read(); } private static async void TestLoops() { for (int i = 0; i < 100; i++) { await TestAsync(i); } } private static Task TestAsync(int i) { return Task.Run(() => TaskToDo(i)); } private async static void TaskToDo(int i) { await Task.Delay(10); Console.WriteLine(i); } } } 

这个答案旨在提供一些特定于ASP.NET的信息。

通过在MVC控制器中使用asynchronous/等待,可以增加线程池的利用率并实现更好的吞吐量,正如下面的文章所解释的那样,

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

在Web应用程序中,在启动时看到大量并发请求,或者突发负载(并发性突然增加)时,使这些Web服务调用asynchronous将提高应用程序的响应速度。 asynchronous请求需要花费与同步请求相同的时间进行处理。 例如,如果某个请求发出的Web服务调用需要两秒钟才能完成,则无论是同步执行还是asynchronous执行,请求都需要两秒钟的时间。 但是,在asynchronous调用期间,线程在等待第一个请求完成时不会阻止对其他请求的响应。 因此,当有许多调用长时间运行的并发请求时,asynchronous请求会阻止请求排队和线程池增长。

说实话,我仍然认为最好的解释是关于未来和维基百科的承诺: http : //en.wikipedia.org/wiki/Futures_and_promises

基本思想是你有一个独立的线程池,asynchronous执行任务。 当使用它。 然而,该对象承诺它会在某个时间执行操作,并在请求时给出结果。 这意味着当你请求结果并且没有完成的时候它会被阻塞,否则在线程池中执行。

从那里你可以优化的东西:一些操作可以实现asynchronous,你可以通过批量处理后续请求和/或重新sorting来优化文件IO和networking通信。 我不确定这是否已经在微软的任务框架中,但如果这不是我将要添加的第一件事情。

您实际上可以在C#4.0中使用yield来实现未来的模式sorting。 如果你想知道它是如何工作的,我可以推荐一个体面的工作链接: http : //code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/ 。 但是,如果你自己开始玩弄,那么你会发现,如果你想做所有的酷事,你真的需要语言支持 – 这正是微软所做的。

所有的答案在这里使用Task.Delay()或其他内置的asynchronous函数。 但是这里是我的例子,不使用这些asynchronous函数:

  // Starts counting to a large numbewr and then immediately displays message "i'm counting...". // Then it waits for task to finish and displays "finished, press any key". static void asyncTest () { Console.WriteLine("Started asyncTest()"); Task<long> task = asyncTest_count(); Console.WriteLine("Started counting, please wait..."); task.Wait(); // if you comment this line you will see that message "Finished counting" will be displayed before we actually finished counting. //Console.WriteLine("Finished counting to " + task.Result.ToString()); // using task.Result seems to also call task.Wait(). Console.WriteLine("Finished counting."); Console.WriteLine("Press any key to exit program."); Console.ReadLine(); } static async Task<long> asyncTest_count() { long k = 0; Console.WriteLine("Started asyncTest_count()"); await Task.Run(() => { long countTo = 100000000; int prevPercentDone = -1; for (long i = 0; i <= countTo; i++) { int percentDone = (int)(100 * (i / (double)countTo)); if (percentDone != prevPercentDone) { prevPercentDone = percentDone; Console.Write(percentDone.ToString() + "% "); } k = i; } }); Console.WriteLine(""); Console.WriteLine("Finished asyncTest_count()"); return k; } 

使用它们等于产卵后台线程来执行长期的逻辑?

本文MDSN:使用asynchronous和等待的asynchronous编程(C#)明确解释:

asynchronous和等待关键字不会导致创build额外的线程。 asynchronous方法不需要multithreading,因为asynchronous方法不能在自己的线程上运行。 该方法在当前同步上下文上运行,并仅在该方法处于活动状态时才在线程上使用时间。

我理解的方式也是,应该有第三个术语join混合: Task

Async只是一个限定符,你把你的方法说这是一个asynchronous方法。

Taskasync函数的返回。 它asynchronous执行。

await一个任务。 当代码执行到这一行时,控件跳回到你周围的原始函数的调用者。

相反,如果将一个async函数(即Task )的返回值赋给一个variables,当代码执行到达这一行时,它将继续在该函数的Task执行, 同时Taskasynchronous执行。

在下面的代码中,HttpClient方法GetByteArrayAsync返回一个Task,getContentsTask。 该任务是在任务完成时产生实际字节数组的承诺。 将await运算符应用于getContentsTask,以在SumPageSizesAsync中暂停执行,直到getContentsTask完成。 同时,控件返回给SumPageSizesAsync的调用者。 getContentsTask完成后,awaitexpression式计算为一个字节数组。

 private async Task SumPageSizesAsync() { // To use the HttpClient type in desktop apps, you must include a using directive and add a // reference for the System.Net.Http namespace. HttpClient client = new HttpClient(); // . . . Task<byte[]> getContentsTask = client.GetByteArrayAsync(url); byte[] urlContents = await getContentsTask; // Equivalently, now that you see how it works, you can write the same thing in a single line. //byte[] urlContents = await client.GetByteArrayAsync(url); // . . . }