根据我的理解,async和await所做的主要事情之一是使代码易于编写和阅读-但使用它们是否等于生成后台线程来执行长时间的逻辑?
我目前正在尝试最基本的例子。我内联添加了一些注释。你能给我解释一下吗?
// 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;
}
这个答案旨在提供一些特定于ASP.NET的信息。
通过在MVC控制器中使用async/await,可以提高线程池的利用率,并实现更好的吞吐量,如下文所述。
http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4
在web应用程序中看到大量的并发请求
启动或有突发负载(并发性突然增加),
使这些web服务调用异步将增加
应用程序的响应性。异步请求采用
处理同步请求所需的时间相同。例如,
如果一个请求进行了web服务调用,需要两秒钟才能完成
完成后,无论执行与否,请求都需要两秒钟
同步或异步。然而,在异步调用期间,
线程在运行时不会阻塞对其他请求的响应
等待第一个请求完成。因此,异步
请求阻止了请求队列和线程池的增长
调用长时间运行的操作的许多并发请求。
说实话,我仍然认为最好的解释是维基百科上关于未来和承诺的解释:http://en.wikipedia.org/wiki/Futures_and_promises
基本思想是,您拥有一个单独的异步执行任务的线程池。使用时。然而,对象承诺它将在某个时间执行操作,并在您请求时给您结果。这意味着当您请求结果并且还没有完成时,它将阻塞,否则将在线程池中执行。
从那里你可以优化事情:一些操作可以异步实现,你可以优化像文件IO和网络通信通过批处理后续请求和/或重新排序它们。我不确定这是否已经在微软的任务框架中-但如果不是,这将是我首先要添加的事情之一。
实际上,您可以在c# 4.0中实现这种未来模式。如果你想知道它到底是如何工作的,我可以推荐这个链接:http://code.google.com/p/fracture/source/browse/trunk/Squared/TaskLib/。但是,如果您自己开始使用它,您将注意到如果您想做所有很酷的事情,您确实需要语言支持——这正是微软所做的。
异步&等待简单的解释
简单的类比
一个人可能会等早班火车。这就是他们所做的一切,因为这是他们目前正在执行的主要任务。(同步编程(你通常做的事情!))
另一些人可能在等早班火车的时候,抽着烟,喝着咖啡。(异步编程)
什么是异步编程?
异步编程是指程序员选择在与主线程执行分开的线程上运行一些代码,然后在执行完成时通知主线程。
async关键字实际上做什么?
在方法名前加上async关键字,例如
async void DoSomething(){ . . .
允许程序员在调用异步任务时使用await关键字。这就是它所做的。
为什么这很重要?
在许多软件系统中,主线程专门用于与用户界面相关的操作。如果我正在运行一个非常复杂的递归算法,需要5秒才能在我的计算机上完成,但我在主线程(UI线程)上运行这个,当用户试图单击我的应用程序上的任何东西时,它将出现冻结,因为我的主线程已经排队,目前正在处理太多的操作。因此,主线程无法处理从鼠标点击到从按钮点击运行的方法。
什么时候使用Async和Await?
在不涉及用户界面的情况下,最好使用异步关键字。
假设你正在编写一个程序,允许用户在手机上画草图,但是每隔5秒它就会在互联网上查看天气。
我们应该等待呼叫,轮询每5秒呼叫一次网络以获取天气,因为应用程序的用户需要保持与移动触摸屏的交互以绘制漂亮的图片。
如何使用Async和Await
从上面的例子,下面是一些如何编写它的伪代码:
//ASYNCHRONOUS
//this is called using the await keyword every 5 seconds from a polling timer or something.
async Task CheckWeather()
{
var weather = await GetWeather();
//do something with the weather now you have it
}
async Task<WeatherResult> GetWeather()
{
var weatherJson = await CallToNetworkAddressToGetWeather();
return deserializeJson<weatherJson>(weatherJson);
}
//SYNCHRONOUS
//This method is called whenever the screen is pressed
void ScreenPressed()
{
DrawSketchOnScreen();
}
附加说明-更新
我忘了在我最初的笔记中提到,在c#中,你只能等待被包裹在任务中的方法。例如,你可以等待这个方法:
// awaiting this will return a string.
// calling this without await (synchronously) will result in a Task<string> object.
async Task<string> FetchHelloWorld() {..
你不能等待不是任务的方法,像这样:
async string FetchHelloWorld() {..
请随意查看Task类的源代码。
在更高的层次上:
1) Async关键字启用等待,这就是它所做的一切。Async关键字不会在单独的线程中运行该方法。beginf async方法同步运行,直到它命中一个耗时任务的await。
2)你可以等待一个返回Task或t类型Task的方法。你不能等待async void方法。
3)主线程遇到等待耗时任务或实际工作开始时,主线程返回到当前方法的调用方。
4)如果主线程看到一个仍在执行的任务在等待,它不会等待它,而是返回到当前方法的调用者。通过这种方式,应用程序保持响应性。
5)等待处理任务,现在将在线程池的独立线程上执行。
6)当这个await任务完成时,它下面的所有代码将由单独的线程执行
下面是示例代码。执行它并检查线程id
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncAwaitDemo
{
class Program
{
public static async void AsynchronousOperation()
{
Console.WriteLine("Inside AsynchronousOperation Before AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
//Task<int> _task = AsyncMethod();
int count = await AsyncMethod();
Console.WriteLine("Inside AsynchronousOperation After AsyncMethod Before Await, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
//int count = await _task;
Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await Before DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
DependentMethod(count);
Console.WriteLine("Inside AsynchronousOperation After AsyncMethod After Await After DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
}
public static async Task<int> AsyncMethod()
{
Console.WriteLine("Inside AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
int count = 0;
await Task.Run(() =>
{
Console.WriteLine("Executing a long running task which takes 10 seconds to complete, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(20000);
count = 10;
});
Console.WriteLine("Completed AsyncMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
return count;
}
public static void DependentMethod(int count)
{
Console.WriteLine("Inside DependentMethod, Thread Id: " + Thread.CurrentThread.ManagedThreadId + ". Total count is " + count);
}
static void Main(string[] args)
{
Console.WriteLine("Started Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
AsynchronousOperation();
Console.WriteLine("Completed Main method, Thread Id: " + Thread.CurrentThread.ManagedThreadId);
Console.ReadKey();
}
}
}
当使用async和await时,编译器在后台生成一个状态机。
下面是一个例子,我希望我能解释一些正在发生的高级细节:
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;
}
好的,这里发生了什么:
Task<int> longRunningTask = LongRunningOperationAsync();开始执行LongRunningOperation
独立的工作完成了,假设主线程(线程ID = 1),然后等待longRunningTask到达。
现在,如果longRunningTask还没有完成,它仍在运行,MyMethodAsync()将返回到它的调用方法,因此主线程不会被阻塞。当longRunningTask完成时,来自ThreadPool的线程(可以是任何线程)将返回到MyMethodAsync()之前的上下文中并继续执行(在这种情况下将结果打印到控制台)。
第二种情况是longRunningTask已经完成执行,结果可用。当到达await longRunningTask时,我们已经有了结果,所以代码将继续在同一线程上执行。(在本例中将结果打印到控制台)。当然,对于上面的例子,情况并非如此,其中涉及到Task.Delay(1000)。