谁能解释一下这个await
功能是做什么的?
5 回答
他们昨天刚刚在 PDC 上谈过这个!
Await 与 .NET 中的任务(并行编程)结合使用。它是 .NET 下一版本中引入的关键字。它或多或少让您“暂停”方法的执行以等待 Task 完成执行。这是一个简短的例子:
//create and run a new task
Task<DataTable> dataTask = new Task<DataTable>(SomeCrazyDatabaseOperation);
//run some other code immediately after this task is started and running
ShowLoaderControl();
StartStoryboard();
//this will actually "pause" the code execution until the task completes. It doesn't lock the thread, but rather waits for the result, similar to an async callback
// please so also note, that the task needs to be started before it can be awaited. Otherwise it will never return
dataTask.Start();
DataTable table = await dataTask;
//Now we can perform operations on the Task result, as if we're executing code after the async operation completed
listBoxControl.DataContext = table;
StopStoryboard();
HideLoaderControl();
基本上,async
andawait
关键字允许您指定方法的执行应该在所有使用await
标记异步方法调用时停止,然后在异步操作完成后恢复。这允许您在应用程序的主线程中调用方法并异步处理复杂的工作,而无需显式定义线程和连接或阻塞应用程序的主线程。
认为它有点类似于yield return
生成 IEnumerable 的方法中的语句。当运行时命中时yield
,它基本上会保存方法的当前状态,并返回正在产生的值或引用。下一次在返回对象(由运行时内部生成)上调用 IEnumerator.MoveNext() 时,方法的旧状态将恢复到堆栈中,并在之后的下一行继续执行,yield return
就好像我们从未离开过方法。如果没有此关键字,则必须自定义 IEnumerator 类型来存储状态和处理迭代请求,其方法确实会变得非常复杂。
同样,标记为的方法async
必须至少有一个await
. 在 anawait
上,运行时将保存当前线程的状态和调用堆栈,进行异步调用,然后展开回运行时的消息循环以处理下一条消息并保持应用程序响应。当异步操作完成时,在下一个调度机会时,向上异步操作的调用堆栈被推回并继续,就好像调用是同步的一样。
因此,这两个新关键字基本上简化了异步流程的编码,就像yield return
简化了自定义枚举的生成一样。使用几个关键字和一点背景知识,您可以跳过传统异步模式中所有令人困惑且经常出错的细节。这在几乎任何事件驱动的 GUI 应用程序(如 Winforms、Silverlight 的 WPF)中都是无价的。
当前接受的答案具有误导性。
await
没有暂停任何东西。首先,它只能在方法或标记为async
并返回 的 lambda 中使用,Task
或者void
如果您不关心Task
在此方法中运行实例。
这是一个插图:
internal class Program
{
private static void Main(string[] args)
{
var task = DoWork();
Console.WriteLine("Task status: " + task.Status);
Console.WriteLine("Waiting for ENTER");
Console.ReadLine();
}
private static async Task DoWork()
{
Console.WriteLine("Entered DoWork(). Sleeping 3");
// imitating time consuming code
// in a real-world app this should be inside task,
// so method returns fast
Thread.Sleep(3000);
await Task.Run(() =>
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("async task iteration " + i);
// imitating time consuming code
Thread.Sleep(1000);
}
});
Console.WriteLine("Exiting DoWork()");
}
}
输出:
进入 DoWork()。Sleeping 3
异步任务迭代 0
任务状态:WaitingForActivation
等待 ENTER
异步任务迭代 1
异步任务迭代 2
异步任务迭代 3
异步任务迭代 4
异步任务迭代 5
异步任务迭代 6
异步任务迭代 7
异步任务迭代 8
异步任务迭代 9
退出做工作()
对于 .NET 中的异步编程新手来说,在您可能更熟悉的场景中,这里有一个(完全是假的)类比 - 使用 JavaScript/jQuery 的 AJAX 调用。一个简单的 jQuery AJAX 帖子如下所示:
$.post(url, values, function(data) {
// AJAX call completed, do something with returned data here
});
我们在回调函数中处理结果的原因是我们在等待 AJAX 调用返回时不会阻塞当前线程。只有当响应准备好时,回调才会被触发,同时释放当前线程来做其他事情。
现在,如果 JavaScript 支持await
关键字(当然它不支持(还没有!)),您可以通过以下方式实现相同的效果:
var data = await $.post(url, values);
// AJAX call completed, do something with returned data here
这更简洁,但看起来我们确实引入了同步的阻塞代码。但是(假的)JavaScript 编译器会获取之后的所有内容await
并将其连接到回调中,因此在运行时第二个示例的行为与第一个示例一样。
它可能看起来并没有为您节省很多工作,但是当涉及到异常处理和同步上下文等事情时,编译器实际上为您做了很多繁重的工作。更多信息,我推荐Stephen Cleary 的博客系列后面的常见问题解答。
如果我必须在 Java 中实现它,它看起来像这样:
/**
* @author Ilya Gazman
*/
public abstract class SynchronizedTask{
private ArrayList<Runnable> listeners = new ArrayList<Runnable>();
private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(6, 6, 0, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1000));
public final void await(Runnable listener){
synchronized (this) {
listeners.add(listener);
}
}
public void excecute(){
onExcecute();
for (int i = listeners.size() - 1; i >= 0; i--) {
Runnable runnable;
synchronized (this) {
runnable = listeners.remove(i);
}
threadPoolExecutor.execute(runnable);
}
}
protected abstract void onExcecute();
}
您的应用程序会像这样使用它:
public class Test{
private Job job = new Job();
public Test() {
craeteSomeJobToRunInBackground();
methode1();
methode2();
}
private void methode1(){
System.out.println("Running methode 1");
job.await(new Runnable() {
@Override
public void run() {
System.out.println("Continue to running methode 1");
}
});
}
private void methode2(){
System.out.println("Running methode 2");
}
private void craeteSomeJobToRunInBackground() {
new Thread(new Runnable() {
@Override
public void run() {
job.excecute();
}
}).start();
}
private class Job extends SynchronizedTask{
@Override
protected void onExcecute() {
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Job is done");
}
}
}