使用下面的代码我希望字符串"Finished"出现在控制台上的"Ready"之前.任何人都可以向我解释,为什么等待完成这个样本中的任务?
static void Main(string[] args)
{
TestAsync();
Console.WriteLine("Ready!");
Console.ReadKey();
}
private async static void TestAsync()
{
await DoSomething();
Console.WriteLine("Finished");
}
private static Task DoSomething()
{
var ret = Task.Run(() =>
{
for (int i = 1; i < 10; i++)
{
Thread.Sleep(100);
}
});
return ret;
}
Run Code Online (Sandbox Code Playgroud)
The*_*ung 28
你在"准备好"之后看到"完成"的原因!是因为异步方法常见的混淆点,与SynchronizationContexts无关.SynchronizationContext控制哪些线程运行,但'async'有自己非常具体的有关排序的规则.程序会变得疯狂!:)
'await'确保当前异步方法中的其余代码在等待完成之后才会执行.它不会对呼叫者做出任何承诺.
您的异步方法返回'void',它适用于不允许原始调用方依赖方法完成的异步方法.如果您希望调用者也等待,您需要确保您的异步方法返回一个Task(如果您只想要完成/例外),或者Task<T>如果您确实想要返回一个值.如果将方法的返回类型声明为这两者中的任何一个,那么编译器将负责其余的工作,即生成表示该方法调用的任务.
例如:
static void Main(string[] args)
{
Console.WriteLine("A");
// in .NET, Main() must be 'void', and the program terminates after
// Main() returns. Thus we have to do an old fashioned Wait() here.
OuterAsync().Wait();
Console.WriteLine("K");
Console.ReadKey();
}
static async Task OuterAsync()
{
Console.WriteLine("B");
await MiddleAsync();
Console.WriteLine("J");
}
static async Task MiddleAsync()
{
Console.WriteLine("C");
await InnerAsync();
Console.WriteLine("I");
}
static async Task InnerAsync()
{
Console.WriteLine("D");
await DoSomething();
Console.WriteLine("H");
}
private static Task DoSomething()
{
Console.WriteLine("E");
return Task.Run(() =>
{
Console.WriteLine("F");
for (int i = 1; i < 10; i++)
{
Thread.Sleep(100);
}
Console.WriteLine("G");
});
}
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,"A"到"K"将按顺序打印出来.这是发生了什么:
"A":在调用其他任何东西之前
"B":正在调用OuterAsync(),Main()仍在等待.
"C":正在调用MiddleAsync(),OuterAsync()仍在等待查看MiddleAsync()是否完整.
"D":正在调用InnerAsync(),MiddleAsync()仍在等待看看InnerAsync()是否完整.
"E":正在调用DoSomething(),InnerAsync()仍在等待DoSomething()是否完成.它会立即返回一个并行启动的任务.
由于并行性,InnerAsync()完成了对DoSomething()返回的任务的完整性测试以及实际启动的DoSomething()任务之间的竞争.
一旦DoSomething()启动,它打印出"F",然后休眠一秒钟.
同时,除非线程调度超级混乱,否则InnerAsync()几乎肯定已经意识到DoSomething()还没有完成.现在异步魔法开始了.
InnerAsync()将自己从callstack中拉出来,并说它的任务是不完整的.这会导致MiddleAsync()从callstack中退出并说它自己的任务不完整.这导致OuterAsync()将自己从callstack中拉出来,并说它的任务也是不完整的.
任务返回Main(),注意到它不完整,Wait()调用开始.
与此同时...
在该并行线程上,在DoSomething()中创建的旧式TPL任务最终完成休眠.它打印出"G".
一旦该任务被标记为完成,其余的InnerAsync()将在TPL上进行调度以再次执行,并打印出"H".这完成了InnerAsync()最初返回的任务.
一旦该任务被标记为完成,其余的MiddleAsync()将在TPL上进行调度以再次执行,并打印出"I".这完成了MiddleAsync()最初返回的任务.
一旦该任务被标记为完成,剩余的OuterAsync()就会在TPL上被调度以再次执行,并打印出"J".这完成了OuterAsync()最初返回的任务.
由于OuterAsync()的任务现在完成,Wait()调用返回,Main()打印出"K".
因此,即使顺序中有一点并行性,C#5异步仍然保证控制台写入按照确切的顺序发生.
让我知道这是否仍然令人困惑:)
Nic*_*ler 16
你在一个控制台应用程序中,所以你没有专门的SynchronizationContext,这意味着你的继续将在线程池线程上运行.
此外,你是不是等待调用TestAsync()在Main.这意味着当您执行此行时:
await DoSomething();
Run Code Online (Sandbox Code Playgroud)
该TestAsync方法返回控制权Main,只是继续正常执行 - 即输出"就绪!" 并等待按键.
同时,第二次DoSomething完成后,awaitin TestAsync将继续在线程池线程上输出"Finished".
| 归档时间: |
|
| 查看次数: |
10881 次 |
| 最近记录: |