`task`上下文可以在第一次`await`之前切换吗?

bfo*_*ops 4 c# async-await

是创建Task一个上下文切换点,还是只在它开始await或做其他异步事情时?

例如,如果我有这些功能:

async Task Foo()
{
  Console.WriteLine("In Foo");
  await bar();
}

void CallFoo()
{
  var task = Foo();
  Console.WriteLine("Returned from Foo");
  task.Wait();
}
Run Code Online (Sandbox Code Playgroud)

在"In Foo"之前,"从Foo返回"是否可以打印?

Eri*_*ert 11

在"In Foo"之前,"从Foo返回"是否可以打印?

在您编写的程序中,不,这是不可能的.

如果你有一个运行工作线程的不同程序,那么你就有了在线程之间排序副作用的所有常见问题."异步/等待"没有什么特别之处,这些问题就会消失.

创建一个Task是一个上下文切换点,还是只有当它开始等待或做其他异步事情时?

让我们通过"上下文切换点"的含义来准确.

C#中的方法可以执行以下四种操作之一:

  • 永远奔跑
  • 正常返回
  • 暂停 - 我认为你的意思是"上下文切换".

标记的方法async可以暂停,但只有在await未完成的情况下才会暂停.等待完成的等待不会暂停,但它可以抛出.

async方法返回的任务很热门.也就是说,异步工作流程开始运行,直到它返回,抛出或挂起.可以使用Task构造函数创建"冷"任务,并且在您调用Start它之前不会启动.通常,除非您编写自己的任务调度程序,否则不会这样做.

请注意,这与迭代器块不同.一个常见的错误是:

IEnumerable<int> GetMeSomeInts(int x) 
{
  if (x < 0)
    throw new SomeException();
  for (int i = 0; i < x; i += i)
    yield return i;
}
Run Code Online (Sandbox Code Playgroud)

如果你说

var nums = GetMeSomeInts(-1); // 1
foreach(int num in nums)      // 2
   ...
Run Code Online (Sandbox Code Playgroud)

然后投掷不会发生在第1行,它发生在第2行!调用时,迭代器块不会运行到第一个yield.它们会立即暂停,直到迭代后才会执行foreach.要小心,特别是如果您在同一程序中使用异步和迭代程序协同程序.


bfo*_*ops 6

C#5.0语言规范:

10.15.1评估任务返回异步功能

调用任务返回异步函数会导致生成返回任务类型的实例.这称为异步函数的返回任务.该任务最初处于不完整状态.

然后评估异步函数体,直到它被挂起(通过到达await表达式)或终止,此时控制权与返回任务一起返回给调用者.

在问题的示例中,Foo将立即对其进行评估,直到await控制权返回给调用者."在Foo中"将始终在"从Foo返回"之前打印.

  • @Crowcoder:肯定是.假设异步工作流表示*从Internet获取值并缓存它*.初始提取可能需要很长时间,因此它应该是异步的,但您不必支付调用方法的*秒*时间的收益; 它应该立即返回缓存的值. (2认同)