【问题标题】:How to make sure a task is started and safely start it if not?如果没有,如何确保任务已启动并安全启动?
【发布时间】:2017-12-04 22:55:36
【问题描述】:

我从某个我无法控制的地方得到IEnumerable<Task> tasks。我不知道这些任务是使用new TaskTask.Run 手动创建的,还是它们是异步方法调用async Task DoSomethingAsync() 的结果。

如果我这样做await Task.WhenAll(tasks),我可能会无限期挂起,因为可能有一项或多项任务没有开始。

我不能这样做tasks.ForEach(t => t.Start()),因为如果它来自异步方法调用(已经启动),我会得到一个 InvalidOperationException “Start may not be called on a promise-style task”。

我不能做await Task.WhenAll(tasks.Select(t => Task.Run(async () => await t))),因为每个t 仍然不能仅仅通过等待它来开始。

我认为解决方案与基于此检查每个任务的StatusStart() 有关,但我也认为这可能很棘手,因为该状态随时可能改变,对吧?如果这仍然是要走的路,检查哪些状态是正确的,我应该担心哪些线程问题?

非工作案例示例:

//making an IEnumerable as an example, remember I don't control this part
Task t = new Task( () => Console.WriteLine("started"));
IEnumerable<Task> tasks = new[] {t};

//here I receive the tasks
await Task.WhenAll(tasks);//waits forever because t is not started

工作案例示例:

//calls the async function, starting it.
Task t = DoSomethingAsync();
IEnumerable<Task> tasks = new[] {t};

//here I receive the tasks and it will complete because the task is already started
await Task.WhenAll(tasks);

async Task DoSomethingAsync() => Console.WriteLine("started");

【问题讨论】:

  • 从来没有什么好的理由去创建一个未启动的任务,当然你没有理由将一个未启动的任务暴露给任何其他代码。我会认为任何返回未启动任务的方法都是错误的。如果是您的代码,请修复它,如果是其他人的代码,请告诉他们他们有错误并且他们需要修复他们的代码。任何返回已启动和未启动任务的mix 的方法都是邪恶的。如果是您的代码,请修复它,如果是其他人的代码,请远离他们并且永远不要再触摸他们的代码,您不能相信它可以正常工作。
  • await Task.WhenAll(tasks.Select(t =&gt; Task.Run(async () =&gt; await t))) 只是一种冗长、迂回且效率较低的写作方式await Task.WhenAll(tasks); 没有理由写前者,除非你的 CPU 太冷并且你需要加热它.
  • @Servy 很重要,我最终可能会简单地记录它期望已启动的任务,而不是尝试处理未启动的任务。
  • 真的没有理由去记录它。您在任何地方编写或与之交互的任何代码都只会期望启动任务,并且永远不会尝试启动任务。每当您处理 TPL 时,它都是隐含的。

标签: c# asynchronous async-await task


【解决方案1】:

如果由于某种原因您无法更改代码以不返回未启动的任务,您可以检查Status 并启动任务(如果它具有Created 状态):

if (task.Status == TaskStatus.Created)
   task.Start();

所有其他任务状态都表明任务已完成、正在运行或正在计划中,因此您无需在该状态下启动任务。

当然,理论上这会引入竞争条件,因为任务可以在您的检查和Start 调用之间开始,但是,正如 Servy 在 cmets 中正确指出的那样 - 如果这里曾经存在竞争条件 - 这意味着另一方(它创建了该任务)试图启动它。即使您处理异常 (InvalidOperationException) - 另一方也不太可能这样做,因此在尝试开始自己的任务时会出现异常。因此,只有一方(您或创建该任务的代码)应该尝试启动它。

也就是说 - 比这样做更好的是确保您可能永远不会得到未开始的任务,因为将此类任务返回到外部代码只是糟​​糕的设计,至少没有明确指出(虽然它是为了某些用途可以在内部使用未启动的任务)。

【讨论】:

  • 如果您担心由其他人启动的竞态条件,那么这意味着 其他人也在尝试启动任务,或者他们 不应该尝试启动它,您需要修复它,或者不应该尝试启动它,因为其他人正在启动它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-31
  • 2018-02-22
  • 2013-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多