有人能解释一下 await
函数是做什么的吗?
有人能解释一下 await
函数是做什么的吗?
他们昨天在PDC上刚刚谈论过这个!
Await与.NET中的任务(并行编程)一起使用。它是一个关键字,将在下一个版本的.NET中引入。它更或多或少地让您“暂停”方法的执行以等待任务完成执行。这里是一个简短的例子:
//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();
await
不会暂停任何东西,它只是在等待的Task
仍在运行时,将方法的其余部分包装在继续任务中。 - Anriasync
和 await
关键字允许你指定方法在所有使用异步方法调用标记的await
处停止执行,然后在异步操作完成时恢复执行。这使得你可以在应用程序的主线程中调用一个方法并异步处理复杂的工作,而无需显式定义线程和连接或阻塞应用程序的主线程。yield return
语句。当运行时遇到yield
时,它将基本保存方法的当前状态,并返回被产生的值或引用。下次在返回对象(由运行时内部生成)上调用IEnumerator.MoveNext()时,方法的旧状态将还原到堆栈中,并按照yield return
之后的下一行继续执行,就好像我们从未离开过该方法一样。如果没有此关键字,则必须自定义IEnumerator类型以存储状态并处理迭代请求,其中的方法可能变得非常复杂。async
的方法必须至少有一个await
。在await
上,运行时会保存当前线程的状态和调用堆栈,进行异步调用,并展开回运行时的消息循环以处理下一条消息,并保持应用程序响应。当异步操作完成时,在下一个调度机会上,异步操作的调用堆栈被推回并继续执行,就像调用是同步的一样。yield return
简化了自定义可枚举对象的生成一样。使用几个关键字和一些背景知识,你可以跳过传统异步模式的混乱和经常容易出错的细节。这在任何事件驱动的GUI应用程序中都将非常重要,例如Winforms、WPF或Silverlight。await
不会暂停任何东西。
首先,它只能在标记为 async
并返回 Task
或 void
的方法或 lambda 中使用,如果你不介意在这个方法中运行 Task
实例,则可以返回 void
。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()
对于任何新接触 .NET 异步编程的人,这里有一个(完全虚构的)类比,使用您可能更熟悉的场景 - 使用 JavaScript/jQuery 进行 AJAX 调用。一个简单的 jQuery AJAX post 如下所示:
$.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
之后的所有内容连接到回调函数中,在运行时第二个示例的行为就像第一个示例一样。
尽管看起来并没有省下太多工作,但当涉及到异常处理和同步上下文时,编译器实际上为您做了很多重活。如果想了解更多,请参考FAQs和Stephen Cleary的博客系列。
/**
* @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");
}
}
}