新的C#等待功能有什么作用?[关闭]


83

谁能解释该await功能的作用?




dotnetperls.com/async上也有不错的例子。
Miljen Mikic,2015年

我不认为这个问题太广泛或不应该被解决。它在问一个关键字的含义。(早期版本是否有所不同?)
Panzercrisis

Answers:


62

他们只是昨天在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();

2
什么时候是C#形式的承诺:en.wikipedia.org/wiki/Futures_and_promises

12
听起来很像Thread.Join()。
史蒂夫·吉迪

10
让我想起了COMEFROM
Joel Spolsky

20
为了完整起见,让我们补充一下,上面的代码必须包装在一个用async关键字修饰的方法中。一旦在其中遇到第一个await关键字,该方法应立即返回。
Przemek,2010年

14
用您的话说:它可以让您“暂停”该方法,但应注意,它不会暂停或阻塞线程。
Matt Crinklaw-Vogt

47

基本上,asyncandawait关键字允许您指定方法的执行应在的所有用法处停止await,这会标记异步方法调用,然后在异步操作完成后恢复执行。这使您可以在应用程序的主线程中调用方法并异步处理复杂的工作,而无需显式定义线程以及联接或阻塞应用程序的主线程。

认为它与yield return产生IEnumerable的方法中的语句有些相似。当运行时命中时yield,它将基本上保存该方法的当前状态,并返回产生的值或引用。下次在返回对象(由运行时内部生成)上调用IEnumerator.MoveNext()时,该方法的旧状态将还原到堆栈中,并且执行继续到下一行,yield return就像我们永远不会离开方法。如果没有此关键字,则必须自定义IEnumerator类型以存储状态并处理迭代请求,而这些方法的确会变得非常复杂。

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

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


31

当前接受的答案具有误导性。 await没有暂停任何事情。首先,它只能在标记为return的方法或lambda中使用asyncTask或者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
退出做工作()


1
您还知道,它将阻塞呼叫者3秒钟,甚至没有给他们他们可以执行的任务await?意思是如果从UI线程调用它会阻塞UI线程3秒钟?这个模型的想法是避免做这样的事情。
Servy

1
@Servy是的,这就是重点。显示执行的所有阶段。这不是真实示例。
杏里

7
@Servy你在骗我吗?
杏里

2
不。我正在努力帮助您改善答案。
Servy

2
@ Anri ...非常感谢您在这里所做的努力。非常感谢!!
Praveen Prajapati 2013年

11

对于.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的博客系列


坚持这个假的比喻(这对我的帮助很大,所以,谢谢!),您所说的“一切”是什么意思?一切都在同一功能(方法)的范围内吗?还是它之后的所有内容都可以添加到调用堆栈中?

2
“之后的一切” =方法的其余部分。编译器有效地将其余方法重写为回调,并且控件立即返回当前方法的调用方。
Todd Menier 2015年

1
辉煌的托德,再次感谢您的解释。我敢肯定,对其他人也很有用。

-2

如果我必须用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");
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.