Task.Run 与 Task.Factory.StartNew - 未发生预期的死锁

Ton*_*ony 4 c# deadlock task async-await

我阅读了 Task.Run 和 Task.Factory.StartNew 的差异。

Task.Run(() => {});
Run Code Online (Sandbox Code Playgroud)

应该相当于

Task.Factory.StartNew(() => {}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
Run Code Online (Sandbox Code Playgroud)

但是在我的代码中,我希望由于 Task.Factory.StartNew 而不会发生死锁:

private Task backgroundTask;

private async Task DoSomethingAsync()
{
   // this should deadlock
   await this.backgroundTask.ConfigureAwait(false);
   throw new Exception();
}

private async Task Test()
{
   this.backgroundTask = Task.Factory.StartNew(async () =>
      {
         await this.DoSomethingAsync().ConfigureAwait(false);
      }, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);

   // just wait here for testing/debugging
   await Task.Delay(10000).ConfigureAwait(false);
   // if no deadlock, this should throw
   await this.backgroundTask.ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)

但这并不是死锁。DoSomethingAsync 中的异常被抛出但从未被捕获。在 Task.Delay 之后等待 Task 也不会抛出,因为它是 RanToCompletion。

使用 Task.Run 时,它按预期死锁:

private Task backgroundTask;

private async Task DoSomethingAsync()
{
   // this is deadlocking
   await this.backgroundTask.ConfigureAwait(false);
   throw new Exception();
}

private async Task Test()
{
   this.backgroundTask= Task.Run(async () =>
      {
         await this.DoSomethingAsync().ConfigureAwait(false);
      });

   // just wait here for testing/debugging
   await Task.Delay(10000).ConfigureAwait(false);
   // never reached because of deadlock
   await this.backgroundTask.ConfigureAwait(false);
}
Run Code Online (Sandbox Code Playgroud)

有人可以解释这种行为吗?

The*_*ias 5

Task.Factory.StartNew与异步委托一起使用时的方法返回嵌套任务:Task<Task>。您将此嵌套任务分配给类型为 的变量Task,这是允许的,因为该类是Task<TResult>从该类派生的Task。然后发生的事情是您失去了对内部任务的引用,因此您无法等待它。当您await this.backgroundTask在等待外部Task<Task>,但无法获得等待操作的结果时,即 a Task、内部Taskawaitit。内在的任务已经变成了即发即忘的任务。

  • @AlteGurke,当委托到达第一个“await”时,外部任务完成,这是因为对委托的调用返回代表异步等待的任务对象。 (2认同)