【问题标题】:Is this how Asynchronous Flow Works这是异步流程的工作原理吗
【发布时间】:2018-10-29 05:47:11
【问题描述】:

我是 c# 异步编程的新手,并且已经阅读了几篇关于它的好文章(参见 hereherehere 示例)。我想我对它的工作原理有所了解,但希望有人能确认我在正确的轨道上。

如果我理解正确的话,异步编程是这样工作的:

将关键字 async 添加到方法中,以表明它会离开并做自己的事情,而与程序的其余部分正在做的事情无关。这基本上就像厨师要求预备厨师去切蘑菇、洋葱和胡萝卜。

async 方法被分配给类型为 Task 的任务。 Task 是正在运行的方法,或者在我们的例子中是准备厨师。返回类型是任务将返回的类型。一旦分配了任务,当前方法就会忽略它,知道它正在处理。

await 命令是调用方法请求任务结果的地方。继续我们的例子,厨师向预备厨师要切碎的蔬菜。如果Task已经完成,则给出结果,调用方法继续运行。如果任务尚未完成,调用方法必须等待它完成。换句话说,如果在厨师需要蔬菜的时候准备厨师还没有完成切菜,那么厨师必须等待才能继续制作食谱。 (我知道,这个比喻在这里不成立,因为真正的厨师会去做其他事情,但这只是为了简化解释。)

要使用 await 命令,方法必须是异步方法。在上面的示例中,调用方法是异步方法。因此,当它遇到 await 命令时,控制会返回到它的调用方法,该方法将继续运行,直到它完成或遇到它自己的 await。

所以,我们现在得到的是方法 A 调用异步方法 B,该方法调用异步方法 C。方法 B 在 await 上停止,等待方法 C 的结果。方法 A 和 C 现在都在异步运行,而方法 B 正在等待结果。一旦方法 C 返回结果,然后方法 A 和 B 将异步运行,直到方法 B 完成,或者方法 A 决定它需要等待方法 B 的结果。请注意,方法 B 可能不需要返回结果,因此方法 A 可能在方法 B 之前完成。

这听起来对吗,或者控制会从 A 转到 B,然后只有在等待被命中时才返回 A?因此,如果 A 没有自己的等待,它将在控制权返回 B 之前完成。

我意识到在复杂性更高的地方我过于简单化了,但我试图在细节之前理解这些概念。

【问题讨论】:

    标签: c# asynchronous async-await


    【解决方案1】:

    无论何时使用await,该方法都会在此时编译为一个单独的方法,并且原始方法在该位置完成(async/await 是语法糖) .生成的方法注册为Task 所指的Task 的延续。如果给定方法中存在多个await,则相同的过程适用于每个await

    任何时候async用来描述一个方法,就意味着该方法支持await并且可以编译成一系列的延续。该方法的第一部分与调用代码内联调用,但作为await 的一部分生成的部分将排队到 TPL 的默认调度程序(按顺序,随着部分完成)并根据该调度程序的配置方式进行调用.从async 方法返回的Task 将代表整个方法(因此可以在方法内的任何await 处取消该方法),并且注册到该Task 的任何延续将排队到TPL 的如果/当 Task 完成时,默认调度程序。

    在你的类比中,厨师更像是一个线程而不是一个方法。我会使用术语“烹饪动作”来描述方法和延续,如果同时完成任何两个烹饪动作,那是因为厨师已将这些动作委托给副厨师长(另一个线程),除非有没有可用的副厨师长,在这种情况下,厨师只能稍后自己做。

    异步/等待

    public void Main()
    {
        var result = Calculate(1, 3).Result;
    
        async Task<int> Calculate(int a, int b)
        {
            var c = await Add(a, 5);
            var d = await Add(b, 3);
    
            return await Add(c, d);
        }
    
        async Task<int> Add(int a, int b)
        {
            return a + b;
        }
    }
    

    纯 TPL

    public void Main()
    {
        const int a = 1;
        const int b = 3;
    
        var t1 = Task.Run(() => new
            {
                c = a + 5
            }
        );
    
        var t2 = t1.ContinueWith(t =>
            new
            {
                c = t.Result.c,
                d = b + 3
            }
        );
    
        var t3 = t2.ContinueWith(t =>
            t.Result.c + t.Result.d
        );
    
        var result = t3.Result;
    }
    

    【讨论】:

    • 谢谢,这是一个非常有用的描述。我有一个澄清要问:如果一个方法被标记为异步,但不包含等待,这是否意味着它将内联运行(作为非异步代码)?
    • 我相信是的。目前我无法引用消息来源,但在第一次等待之前我从未有过异步方法 yield。此外,ReSharper 在声明没有等待的异步时会发出警告,称该方法将同步运行。我知道 ReSharper 不是该主题的权威,但通常 ReSharper 团队知道他们在说什么。
    猜你喜欢
    • 1970-01-01
    • 2012-07-21
    • 2017-04-06
    • 2016-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多