“await”重新生成任务,如果它已经是.Start() - ed?

时间:2017-03-06 23:46:50

标签: c# asynchronous async-await

如果我有以下内容:

// a:
// returns a task, then starts and joins it with "await" keyword
var resultA = await GetBigTask();

// b:
// gets a Task<T>, launches it with Start(), then joins it with "await"
var myBigTask = GetBigTask();
myBigTask.Start();
var resultB = await myBigTask;

为了清楚起见,我的评论是否正确?

在尚未启动的任务上使用“await”关键字(如从GetBigTask()返回的匿名任务)将启动它然后加入它。在已经是.Start()的任务上使用“await”关键字将不会启动它的第二个实例,而是将它加入现有的实例?

因此,如果我有许多完全独立的大任务,那么像“c”而不是“d”那样启动它们总是有益的吗?

// c:
var taskA = GetTaskA();
var taskB = GetTaskB();
var taskC = GetTaskC();

// they are all launched in parallel
taskA.Start(); taskB.Start(); taskC.Start();

// then joined together at the end
await taskA; await taskB; await taskC;

// d:
await GetTaskA();
await GetTaskB();  // not launched until GetTaskA() concludes!
await GetTaskC();  // not launched until GetTaskA() AND B concludes!

这一切都正确吗?如果没有,我如何实现“c”,即一次启动多个(独立)任务,等待所有任务?

1 个答案:

答案 0 :(得分:4)

如果没有明确说明您问题的好Minimal, Complete, and Verifiable code example,就无法确定您所展示的代码会发生什么。也就是说,你的问题中至少有一些误解:

  
      
  • 误解#1:await开始一项任务。
  •   
  • 误解#2:await加入一个主题。
  •   

如果使用await等待尚未启动的任务,则它本身不会启动任务。它将永远等待,除非在其他地方你有一些代码可以启动任务。

就“连接”而言,该字通常用于描述当前线程等待某个其他线程或任务的操作。这与await所做的正好相反(是的,我意识到命名法有点令人困惑)。当您的执行到达await语句时,当前正在执行的方法在该点返回,允许该线程继续执行。如果正在等待的等待完成,则在await语句后立即执行 resume

但在此之前,该线程中的执行不会被阻止(或“加入”)。

await语句还有一些其他有用的行为:当等待的操作完成时,如果它成功完成,结果值将被await表达式检索并返回。如果操作不成功而是抛出异常,则异常将被await表达式解压缩并重新抛出。

在您的示例中,如果GetBigTask()字面上返回尚未启动的任务对象,那么仅使用await GetBigTask()将无法执行任何有用的操作,因为等待将永远不会完成。如果任务对象已经已经启动,那么调用myBigTask.Start()将引发异常。

通常,返回任务对象的异步方法将以已经运行的状态返回这些对象。所以你的问题可能反映了第三种误解:

  
      
  • 误解#3:GetBigTask()GetTaskA()等方法会返回尚未启动的任务对象。
  •   

当然,如果没有完整的代码示例,则无法确定。但假设这些方法是由熟悉C#中常用异步习惯用法的人编写的,则无需在返回的对象上调用Start()

如果确实如此,那么只需执行以下操作即可:

var taskA = GetTaskA();
var taskB = GetTaskB();
var taskC = GetTaskC();

await taskA;
await taskB;
await taskC;

GetTask...()方法将以已运行状态返回所有任务。按顺序调用await将按顺序处理每个任务的完成,在完成所有三个任务之前不会继续执行其余任务。