谁能解释该await
功能的作用?
谁能解释该await
功能的作用?
Answers:
他们只是昨天在PDC上谈论了这一点!
.NET中将Await与Tasks(并行编程)结合使用。这是在.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
。在上await
,运行时将保存当前线程的状态和调用堆栈,进行异步调用,然后退回到运行时的消息循环以处理下一条消息并保持应用程序的响应能力。异步操作完成后,在下一个调度时机,将向上执行异步操作的调用堆栈推回并继续进行,就好像该调用是同步的一样。
因此,这两个新关键字基本上简化了异步过程的编码,就像yield return
简化了自定义枚举的生成一样。有了几个关键字和一些背景知识,您就可以跳过传统异步模式的所有令人困惑且经常容易出错的细节。在几乎任何事件驱动的GUI应用程序(例如Winforms,Silverlight的WPF)中,这都是无价的。
当前接受的答案具有误导性。
await
没有暂停任何事情。首先,它只能在标记为return的方法或lambda中使用async
, 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()。休眠3
异步任务迭代0
任务状态:WaitingForActivation
等待ENTER
异步任务迭代1
异步任务迭代2
异步任务迭代3
异步任务迭代4
异步任务迭代5
异步任务迭代6
异步任务迭代7
异步任务迭代8
异步任务迭代9
退出做工作()
await
?意思是如果从UI线程调用它会阻塞UI线程3秒钟?这个模型的想法是避免做这样的事情。
对于.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
并连接到回调中,因此在运行时,第二个示例的行为与第一个示例相同。
看起来似乎并没有节省很多工作,但是当涉及到诸如异常处理和同步上下文之类的内容时,编译器实际上为您做了很多繁重的工作。有关更多信息,我建议您在FAQ后面加上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");
}
}
}