创建Task
是上下文切换点,还是仅在它开始await
或进行其他异步操作时创建?
例如,如果我具有以下功能:
async Task Foo()
{
Console.WriteLine("In Foo");
await bar();
}
void CallFoo()
{
var task = Foo();
Console.WriteLine("Returned from Foo");
task.Wait();
}
“从Foo返回”是否可以在“ In Foo”之前打印?
答案 0 :(得分:11)
“从Foo返回”是否可以在“ In Foo”之前打印?
在您编写的程序中,不行,这是不可能的。
如果您有运行工作者线程的其他程序,那么将遇到在线程之间排序副作用的所有常见问题。 “异步/等待”没有什么特别的可以解决这些问题。
创建Task是上下文切换点,还是仅当它开始等待或执行其他异步操作时?
我们用“上下文切换点”的含义来精确地说。
C#中的方法可以执行以下四项操作之一:
标记为async
的方法可以挂起,但是只有在await
尚未完成时才挂起。等待已完成的等待不会暂停,但是会抛出。
由async
方法返回的任务是 hot 。也就是说,异步工作流开始并运行,直到返回,抛出或挂起。可以使用Task
构造函数创建一个“冷”任务,该任务直到您调用Start
才开始。通常,除非您正在编写自己的任务计划程序,否则您不会这样做。
请注意,这与迭代器块不同。一个常见的错误是:
IEnumerable<int> GetMeSomeInts(int x)
{
if (x < 0)
throw new SomeException();
for (int i = 0; i < x; i += i)
yield return i;
}
如果你说
var nums = GetMeSomeInts(-1); // 1
foreach(int num in nums) // 2
...
然后,在第1行上不发生投掷,而在第2行上发生投掷!调用迭代器块时,它们不会运行到第一个yield
。它们立即挂起,直到在foreach
中进行迭代才执行。请特别注意,如果您在同一程序中同时使用异步和迭代协程。
答案 1 :(得分:6)
10.15.1评估返回任务的异步功能
调用返回任务的异步函数会导致生成返回任务类型的实例。这称为异步功能的 返回任务 。该任务最初处于不完整状态。
然后对异步函数主体进行评估,直到它被挂起(通过到达await表达式)或终止为止,此时控制权连同返回任务一起返回给调用者。
在问题的示例中,Foo
将立即求值,直到它的第一个await
为止,然后才将控制权返回给调用者。 “从Foo返回之前”将始终打印“在Foo中”。