我很好奇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();
}
答案 0 :(得分:3)
同步执行await
方法中async
之前的部分。所有async
方法都是如此。
我们假设await DoDBWorkAsync()
代替await Task.Delay(1000)
{。}}。
这意味着MethodOneAsync
开始运行,执行DoSomeWork
并调用MethodTwoAsync
,DoSomeWork
执行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上查看更多信息: