问题描述
使用code以下我期望字符串完成之前准备好在控制台上出现。可能有人向我解释,为什么等待不会等待完成任务,在此示例中?
静态无效的主要(字串[] args)
{
TestAsync();
Console.WriteLine(准备好了!);
Console.ReadKey();
}
私人异步静态无效TestAsync()
{
等待DoSomething的();
Console.WriteLine(完成);
}
私有静态任务DoSomething的()
{
VAR RET = Task.Run(()=>
{
的for(int i = 1;我小于10;我++)
{
Thread.sleep代码(100);
}
});
返回RET;
}
你之所以现在看到完成后,准备好了!是因为与异步方法共同的困惑点,并有无关SynchronizationContexts。 SynchronizationContext的控制哪个线程运行的东西,但异步大约有订购自己的非常具体的规定。否则程序会发疯! :)
等待保证的code在当前的异步方法休息不至期待已久的事情完成之后执行。它不承诺对任何来电
您异步方法返回无效,其目的是为异步方法不允许对原调用依靠的方法完成。如果你希望你的来电者还等着,你需要确保你的异步方法返回一个工作
(如果你只希望看到完成/异常),或任务< T>
如果你真的想返回一个值,以及。如果你声明的方法的返回类型为上述两种的,那么编译器将采取其他的事情,大约产生了重新presents的方法调用的任务。
例如:
静态无效的主要(字串[] args)
{
Console.WriteLine(A);
//在.NET中,主()必须是无效,经过程序终止
// main()返回。因此,我们必须做一个老式的等待()在这里。
OuterAsync()等待()。
Console.WriteLine(K);
Console.ReadKey();
}
静态异步任务OuterAsync()
{
Console.WriteLine(B);
等待MiddleAsync();
Console.WriteLine(J);
}
静态异步任务MiddleAsync()
{
Console.WriteLine(C);
等待InnerAsync();
Console.WriteLine(I);
}
静态异步任务InnerAsync()
{
Console.WriteLine(D);
等待DoSomething的();
Console.WriteLine(H);
}
私有静态任务DoSomething的()
{
Console.WriteLine(E);
返回Task.Run(()=>
{
Console.WriteLine(F);
的for(int i = 1;我小于10;我++)
{
Thread.sleep代码(100);
}
Console.WriteLine(G);
});
}
在上面的code,A到K将依次打印出来。这里是发生了什么:
A:在任何其他被称为
B:OuterAsync()被调用,主()仍在等待
C:MiddleAsync()被调用,OuterAsync()仍然在等待,看是否MiddleAsync()是完整的还是不
D:InnerAsync()被调用,MiddleAsync()仍然在等待,看是否InnerAsync()是完整的还是不
E:DoSomething的()被调用,InnerAsync()仍然在等待,看是否DoSomething的()完成与否。它会立即返回一个任务,在开始的的并行的
由于并行的,还有InnerAsync之间()完成的测试由DoSomething的()返回的任务,完整性比赛,和DoSomething的()任务实际出发。
在DoSomething的()开始,它打印出F,然后睡一秒钟。
在此期间,除非线程调度超级搞砸了,InnerAsync()几乎可以肯定已经意识到,DoSomething的()是的的还没有完成的。现在异步魔术开始。
InnerAsync()洋基自行关闭调用堆栈,并表示,它的任务是不完整的。这将导致MiddleAsync(),以抽出自行关闭调用堆栈,并说自己的任务是不完整的。这将导致OuterAsync(),以抽出自行关闭调用堆栈,并说,它的任务是不完整的为好。
任务返回到主()这个方法注意到它是不完整的,和wait()调用开始。
同时... 的
在平行线,旧式的DoSomething的创建TPL任务()最终完成睡觉。它打印出G。
在该任务被标记为已完成,InnerAsync的其余部分()被安排在第三方物流得到再次执行,并打印出H。这就完成了最初由InnerAsync()。
返回的任务在是的任务被标注齐全,MiddleAsync的其余部分()被安排在第三方物流得到再次执行,并打印出我。这就完成了最初由MiddleAsync()。
返回的任务在是的任务被标注齐全,OuterAsync的其余部分()被安排在第三方物流得到再次执行,并打印出J。这就完成了最初由OuterAsync()。
返回的任务由于OuterAsync()的任务已经完成,等待()调用返回,和主()打印出K。
因此,即使与并行顺序一点点,C#5异步仍然保证了控制台写作发生了正确的顺序。
让我知道如果这仍然显得扑朔迷离:)
Using the code below I expect the string "Finished" to appear before "Ready" on the console. Could anybody explain to me, why await will not wait for finishing the task in this sample?
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;
}
The reason why you're seeing "Finished" after "Ready!" is because of a common confusion point with async methods, and has nothing to do with SynchronizationContexts. SynchronizationContext control which thread things run on, but 'async' has its own very specific rules about ordering. Otherwise programs would go crazy! :)
'await' ensures that the rest of the code in the current async method doesn't execute until after the thing awaited completes. It doesn't promise anything about the caller.
Your async method returns 'void', which is intended for async methods that don't allow for the original caller to rely on method completion. If you want your caller to also wait, you'll need to make sure your async method returns a Task
(in case you only want completion/exceptions observed), or a Task<T>
if you actually want to return a value as well. If you declare the return type of the method to be either of those two, then the compiler will take care of the rest, about generating a task that represents that method invocation.
For example:
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");
});
}
In the above code, "A" through "K" will print out in order. Here's what's going on:
"A": Before anything else gets called
"B": OuterAsync() is being called, Main() is still waiting.
"C": MiddleAsync() is being called, OuterAsync() is still waiting to see if MiddleAsync() is complete or not.
"D": InnerAsync() is being called, MiddleAsync() is still waiting to see if InnerAsync() is complete or not.
"E": DoSomething() is being called, InnerAsync() is still waiting to see if DoSomething() is complete or not. It immediately returns a task, which starts in parallel.
Because of parallelism, there is a race between InnerAsync() finishing its test for completeness on the task returned by DoSomething(), and the DoSomething() task actually starting.
Once DoSomething() starts, it prints out "F", then sleeps for a second.
In the meanwhile, unless thread scheduling is super messed up, InnerAsync() almost certainly has now realized that DoSomething() is not yet complete. Now the async magic starts.
InnerAsync() yanks itself off the callstack, and says that its task is incomplete.This causes MiddleAsync() to yank itself off the callstack and say that its own task is incomplete.This causes OuterAsync() to yank itself off the callstack, and say that its task is incomplete as well.
The task is returned to Main() which notices it's incomplete, and the Wait() call begins.
meanwhile...
On that parallel thread, the old-style TPL Task created in DoSomething() eventually finishes sleeping. It prints out "G".
Once that task gets marked as complete, the rest of InnerAsync() gets scheduled on the TPL to get executed again, and it prints out "H". That completes the task originally returned by InnerAsync().
Once that task gets marked complete, the rest of MiddleAsync() gets scheduled on the TPL to get executed again, and it prints out "I". That completes the task originally returned by MiddleAsync().
Once that task gets marked complete, the rest of OuterAsync() gets scheduled on the TPL to get executed again, and it prints out "J". That completes the task originally returned by OuterAsync().
Since OuterAsync()'s task is now complete, the Wait() call returns, and Main() prints out "K".
Thus even with a little bit of parallelism in the order, C# 5 async still guarantees that the console writing occurs in that exact order.
Let me know if this still seems confusing :)
这篇关于异步/伺机没有反应如预期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!