异步/等待通过堆栈的行为

时间:2015-04-07 22:31:57

标签: c# .net asynchronous async-await

我很好奇async的流量如何在堆栈中运行。在C#中阅读async时,您将不断阅读以下某些版本:

  

如果我们正在等待的任务尚未完成,请注册   其余的此方法作为该任务的延续,然后返回   立即给你的来电者;该任务将在何时调用延续   它完成了。

立即回复您的来电部分会让我感到困惑。在下面的示例中,假设调用了MethodOneAsync(),执行命中MethodOneAsync中的await。它会立即返回到调用此方法的方法吗?如果是这样,MethodTwoAsync如何执行?或者它是否继续向下移动直到它检测到它实际被阻止(即DoDBWorkAsync())然后产生线程?

public static async Task MethodOneAsync()
{
    DoSomeWork();
    await MethodTwoAsync();
}

public static async Task MethodTwoAsync()
{
    DoSomeWork();
    await MethodThreeAsync();
}

public static async Task MethodThreeAsync()
{
    DoSomeWork();
    await DoDBWorkAsync();
}

3 个答案:

答案 0 :(得分:3)

同步执行await方法中async之前的部分。所有async方法都是如此。

我们假设await DoDBWorkAsync()代替await Task.Delay(1000) {。}}。

这意味着MethodOneAsync开始运行,执行DoSomeWork并调用MethodTwoAsyncDoSomeWork执行MethodThreeAsync,调用DoSomeWork再次执行Task.Delay(1000)

然后它调用Task,取回未完成的MethodTwoAsync并等待它。

等待在逻辑上​​等同于添加延续并将任务返回给调用者,调用者Task执行相同操作并将Task返回给调用者,依此类推。

这样当根延迟public static async Task MethodOneAsync() { DoSomeWorkOne(); await MethodTwoAsync(); DoMoreWorkOne(); } public static async Task MethodTwoAsync() { DoSomeWorkTwo(); await MethodThreeAsync(); DoMoreWorkTwo(); } public static async Task MethodThreeAsync() { DoSomeWorkThree(); await Task.Delay(1000); DoMoreWorkThree(); } 完成时,所有延续都可以一个接一个地运行。


如果我们让你的例子更复杂一点:

public static Task MethodOneAsync()
{
    DoSomeWorkOne();
    DoSomeWorkTwo();
    DoSomeWorkThree();
    return Task.Delay(1000).
        ContinueWith(_ => DoMoreWorkThree()).
        ContinueWith(_ => DoMoreWorkTwo()).
        ContinueWith(_ => DoMoreWorkOne());
}

在逻辑上类似于使用continuation执行此操作:

{{1}}

答案 1 :(得分:1)

首先,让我做一个不同的例子,这样我的代码可以进一步理解

public static async Task MethodOneAsync()
{
    DoSomeWork1();
    await MethodTwoAsync();
    DoOtherWork1();
}

public static async Task MethodTwoAsync()
{
    DoSomeWork2();
    await MethodThreeAsync();
    DoOtherWork2();
}

public static async Task MethodThreeAsync()
{
    DoSomeWork3();
    await DoDBWorkAsync();
    DoOtheWork3();
}

所有async await做的是将上面的代码转换成类似的东西(但即使这是一个巨大的简化)这个

public static Task MethodOneAsync()
{
    DoSomeWork1();
    var syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
    var resultTask = MethodTwoAsync();
    return resultTask.ContinueWith((task) =>
    {
        syncContext.Post((state) =>
        {
            SynchronizationContext.SetSynchronizationContext(syncContext);
            DoOtherWork1();
        }, null);
    });
}

public static Task MethodTwoAsync()
{
    DoSomeWork2();
    var syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
    var resultTask = MethodThreeAsync();
    return resultTask.ContinueWith((task) =>
    {
        syncContext.Post((state) =>
        {
            SynchronizationContext.SetSynchronizationContext(syncContext);
            DoOtherWork2();
        }, null);
    });
}

public static Task MethodThreeAsync()
{
    DoSomeWork3();
    var syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
    var resultTask = DoDbWorkAsync();
    return resultTask.ContinueWith((task) =>
    {
        syncContext.Post((state) =>
        {
            SynchronizationContext.SetSynchronizationContext(syncContext);
            DoOtherWork3();
        }, null);
    });
}.

每个等待只是执行下一层更深,直到它被强制返回一个任务,一旦发生这种情况,它就会在完成任务时开始对任务进行继续,并且在该延续中它传递代表"其余部分功能"到SynchronizationContext.Post(以获取计划执行的代码。

如何安排它取决于你所在的SynchronizationContext。在WPF和Winforms中,它将它排队到消息泵,对于默认值和ASP.NET,它将它排队到线程池工作者。

答案 2 :(得分:0)

在为Task方法触发MethodTwoAsync之后返回(或继续执行,如果立即执行此Task),那么内部{{ 1}}正在执行Task环境。

完成此SynchronizationContext.Current后,.NET状态机会在Task触发后立即将执行返回到该点,并处理其余代码。

MSDN article上查看更多信息:

enter image description here