如果 Task.Wait 尚未运行,它是否或不启动任务?

Dav*_*ten 3 c# task-parallel-library

根据我阅读的 Microsoft TPL 文档(链接),调用该Task.Wait()方法将阻塞当前线程,直到该任务完成(或取消或故障)。但它也表示,如果有问题的任务尚未启动,该Wait方法将尝试通过要求调度程序重新分配它在自己的线程上运行它,从而减少由于阻塞而造成的浪费量。

我有一个系统,其中任务(一旦运行)通过启动其他任务并等待它们的结果来收集数据。反过来,这些其他任务首先从其他任务中收集数据,并且非常强大,可能有几百层深。我真的不希望无数的任务阻塞并等待最后的一项任务最终完成。

但是,当我在测试控制台应用程序中尝试此操作时,Task.Wait()似乎根本没有启动任何东西。

构建一系列任务的正确咒语是什么,这些任务必须以最少的周期浪费相互等待?它有点像 ContinueWith,除了从系列中的最后一个任务开始......

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp1
{
  class Program
  {
    static void Main(string[] args)
    {
      var source = new CancellationTokenSource();
      var token = source.Token;

      // Create a non-running task.
      var task = new Task<string[]>(() => InternalCompute(token), token);

      // Isn't this supposed to start the task?
      task.Wait(CancellationToken.None);

      // I realise this code now won't run until the task finishes,
      // it's here for when I use task.Start() instead of task.Wait().
      Console.WriteLine("Press any key to cancel the process.");
      Console.ReadKey(true);
      source.Cancel();
      Console.WriteLine("Source cancelled...");

      Console.WriteLine("Press any key to quit.");
      Console.ReadKey(true);
    }

    private static string[] InternalCompute(CancellationToken token)
    {
      string[] data;
      try
      {
        data = Compute(token);
      }
      catch (TaskCanceledException ex)
      {
        return null;
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.Message);
        return new[] { ex.Message };
      }

      Console.WriteLine("Post-processor starting.");
      for (int i = 0; i < data.Length; i++)
        if (data[i] is null)
          Console.WriteLine($"Null data at {i}.");
        else
          Console.WriteLine($"Valid data at {i}.");
      Console.WriteLine("Post-processor completed.");
      return data;
    }

    /// <summary>
    /// This method stands in for an abstract one to be implemented by plug-in developers.
    /// </summary>
    private static string[] Compute(CancellationToken token)
    {
      var data = new string[10];
      for (int i = 0; i < data.Length; i++)
      {
        token.ThrowIfCancellationRequested();
        Thread.Sleep(250);
        data[i] = i.ToString();
        Console.WriteLine($"Computing item {i + 1}...");
      }
      return data;
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

Dam*_*ver 6

Tasks 通常分为两组——“冷”任务和“热”任务。“冷”任务是尚未启动且不打算运行的任务。“热”任务是当前可能正在运行或可能未运行的任务,但重要的是,如果它们尚未运行,它们可能随时运行。他们的意思来运行,但尚未分配的资源(线程),他们需要这样做。

什么这篇文章讲的是执行一个“热”的任务,也没有以其他方式运行的机会。“热”任务是通过调用例如创建的Task.Run()。它们也是例如Task您将从异步方法接收到的s类型。new Task(...),另一方面给你“冷”任务。除非或直到您Start在该任务上调用或道德等效方法,否则它仍然是“冷的”。它显式调用其中一种方法,使其“热”而不是“冷”。

一般来说,这些天你根本不想处理“冷”任务,这就是为什么直接调用Task构造函数是不受欢迎的。在他们弄清楚调度应该如何真正工作之前,他们真的是一个糟糕的实验。大多数现代代码根本不希望处理“冷”任务。

上面帖子的关键引用是这个:

但是,如果它还没有开始执行,Wait 可能能够将目标任务从它排队的调度程序中拉出来,并在当前线程上内联执行它。

如果您没有调用Start该任务,则它没有与调度程序一起排队 - 所以显然我们不能做上面所说的。