什么是新的C#等待功能呢?

时间:2010-10-30 05:17:46

标签: c# c#-5.0 async-await

任何人都可以解释await函数的作用吗?

5 个答案:

答案 0 :(得分:61)

他们昨天只是talked about this at PDC

Await与.NET中的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();

答案 1 :(得分:47)

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

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

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

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

答案 2 :(得分:31)

目前接受的答案具有误导性。 await不会暂停任何事情。 首先,如果你不关心在这个方法中运行async实例,它只能用于标记为Task的方法或lambda,并返回voidTask

以下是插图:

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
  等待输入
  异步任务迭代1
  异步任务迭代2
  异步任务迭代3
  异步任务迭代4
  异步任务迭代5
  异步任务迭代6
  异步任务迭代7
  异步任务迭代8
  异步任务迭代9
  退出DoWork()

答案 3 :(得分:10)

对于.NET中异步编程的新手,这里有一个(完全假的)类比,你可能更熟悉的场景 - 使用JavaScript / jQuery的AJAX调用。一个简单的jQuery AJAX帖子如下所示:

$.post(url, values, function(data) {
  // AJAX call completed, do something with returned data here
});

我们在回调函数中处理结果的原因是我们在等待AJAX​​调用返回时不阻塞当前线程。只有当响应准备就绪时,才会触发回调,同时释放当前线程以执行其他操作。

现在,如果JavaScript支持await关键字(当然它不支持(yet!)),您可以实现相同的目标:

var data = await $.post(url, values);
// AJAX call completed, do something with returned data here

这更清晰,但看起来我们引入了同步阻塞代码。但是(假的)JavaScript编译器会把await之后的所有东西都带到回调中,所以在运行时第二个例子的行为就像第一个一样。

它可能看起来似乎没有为您节省太多工作,但是当遇到异常处理和同步上下文之类的事情时,编译器实际上正在为您执行繁重的批次。有关详情,我建议FAQs后跟Stephen Cleary's blog series

答案 4 :(得分:-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");
        }
    }
}