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

Posted

技术标签:

【中文标题】新的 C# 等待功能有啥作用? [关闭]【英文标题】:What's the new C# await feature do? [closed]新的 C# 等待功能有什么作用? [关闭] 【发布时间】:2011-05-02 17:22:19 【问题描述】:

谁能解释await函数的作用?

【问题讨论】:

你指的是这个吗? Asynchronous Programming in C# 5.0 part two: Whence await? 另见Asynchronous Programming with Async and Await dotnetperls.com/async 也有很好的例子。 我不认为这个问题太宽泛或应该关闭。它询问一个关键字的含义。 (早期版本是否有所不同?) 【参考方案1】:

对于不熟悉 .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。

【讨论】:

坚持这个虚假的类比(顺便说一句,这对我帮助很大,所以谢谢!),“之后的一切”是什么意思?一切都只属于同一个函数(方法)吗?或者它之后的所有内容都可以添加到调用堆栈中? "Everything after" = 方法的其余部分。编译器有效地将方法的其余部分重写为回调,并且控制立即返回给当前方法的调用者。 杰出的托德,再次感谢您的解释。我相信对其他人也有用。【参考方案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");
        
    

【讨论】:

【参考方案3】:

当前接受的答案具有误导性。 await 没有暂停任何内容。 首先,它只能在标记为 async 的方法或 lambdas 中使用,如果您不关心在此方法中运行 Task 实例,则返回 Taskvoid

这是一个插图:

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 退出 DoWork()

【讨论】:

你也知道它会阻止调用者 3 秒钟,然后才会给他们他们可以await 执行的任务?这意味着如果从 UI 线程调用它会阻塞 UI 线程 3 秒?这个模型的想法是避免做这样的事情。 @Servy 是的,这就是重点。显示执行的所有阶段。这不是一个真实的例子。 @Servy 你在骗我吗? 不。我正在努力帮助您改进答案。 @Anri ...我非常感谢您在这里所做的努力。非常感谢!!【参考方案4】:

他们昨天刚刚talked about this at PDC!

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

【讨论】:

什么时候是C#形式的promise:en.wikipedia.org/wiki/Futures_and_promises 听起来很像 Thread.Join()。 让我想起了COMEFROM 为了完整起见,让我们补充一点,上面的代码必须包装在一个带有 async 关键字的方法中。一旦在其中遇到第一个 await 关键字,此方法将立即返回。 用你的话说:它可以让你“暂停”方法,但需要注意的是它不会暂停或阻塞线程。【参考方案5】:

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

认为它有点类似于生成 IEnumerable 的方法中的 yield return 语句。当运行时到达yield 时,它基本上会保存方法的当前状态,并返回正在产生的值或引用。下次在返回对象(由运行时内部生成)上调用 IEnumerator.MoveNext() 时,方法的旧状态将恢复到堆栈中,并继续执行 yield return 之后的下一行,就像我们要从未离开过方法。如果没有此关键字,则必须自定义 IEnumerator 类型来存储状态和处理迭代请求,其方法确实会变得非常复杂。

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

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

【讨论】:

以上是关于新的 C# 等待功能有啥作用? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

使用异步/等待 - C# [关闭]

c#开发winform程序,如何做等待窗体?等待窗体上面只有个一gif图片和文字,读取完成后等待窗体关闭。

C# Discord如何等待带有前缀的消息,然后响应它[关闭]

C# 中的 C++ std::async 与异步/等待

Selenium 显式等待在新的 safariDriver 2.48.0 中不起作用

C#的 Task,Thread,ThreadPool 之间有啥异同