新的 C # 等待特性是做什么的?

有人能解释一下 await函数是做什么的吗?

46503 次浏览

他们昨天刚刚 我们在警局谈过这事

中的任务(并行编程)一起使用。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();

基本上,asyncawait关键字允许您指定方法的执行应该在 await的所有使用中停止,await标记异步方法调用,然后在异步操作完成后恢复。这允许你在应用的主线程中调用一个方法并异步地处理复杂的工作,而不需要显式地定义线程和连接或者阻塞应用的主线程。

可以将其看作与生成 IEnumable 的方法中的 yield return语句有些类似。当运行时命中 yield时,它将基本上保存方法的当前状态,并返回正在生成的值或引用。下次使用 IEnumerator。在返回对象(由运行时内部生成)上调用 MoveNext () ,方法的旧状态被恢复到堆栈中,并继续执行 yield return之后的下一行,就好像我们从未离开过方法一样。如果没有这个关键字,IEnumerator 类型必须是自定义的,以存储状态并处理迭代请求,其方法可能会变得非常复杂。

类似地,标记为 async的方法必须至少有一个 await。在 await上,运行时将保存当前线程的状态和调用堆栈,进行异步调用,并退回到运行时的消息循环以处理下一条消息并保持应用程序响应。当异步操作完成时,在下一个调度机会中,将调用堆栈推回异步操作并继续进行,就好像调用是同步的一样。

因此,这两个新关键字基本上简化了异步进程的编码,就像 yield return简化了自定义可枚举数的生成一样。通过使用几个关键字和一点背景知识,您可以跳过传统异步模式中所有令人困惑且容易出错的细节。这在几乎所有事件驱动的 GUI 应用程序中都是无价的,比如 Winforms,Silverlight 的 WPF。

目前公认的答案具有误导性。 await没有暂停任何东西。 首先,它只能在标记为 async并返回 Taskvoid的方法或 lambdas 中使用,如果您不介意在这个方法中运行 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 ()。睡眠3
异步任务迭代0
任务状态: 等待激活
等待进入
异步任务迭代1
异步任务迭代2
异步任务迭代3
异步任务迭代4
异步任务迭代5
异步任务迭代6
异步任务迭代7
异步任务迭代8
异步任务迭代9
退出 DoWork ()

如果我必须在 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");
}
}
}

中的异步编程新手使用。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 的博客系列