Task.WaitAny 退化案例行为

Task.WaitAny degenerate case behaviour

我找不到关于 Task.WaitAny() 在以下情况下的行为的任何文档:

  1. 给定一个空的任务数组。
  2. 给定一组任务,其中一个已经完成。
  3. 给定一系列任务,所有任务都已完成。

我的假设是 2 和 3 会立即 return(#3 选择一个任意索引到 return,可能是第一个或最后一个取决于实现细节但不能保证。)和我希望 1 到 return 是真的吗?

一些简短的单元测试似乎证实了这一点...以下全部通过。 如果有人能找到这种行为的一些正式文件,我会很高兴看到它。 (哪怕只是为了提高自己下次自己定位的能力!)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using System.Threading;

namespace TANTest.Threading
{
  [TestFixture]
  public class MetaTaskTests
  {
    private const int SafeTimeOutMillisecs = 1000;

    [Test, Timeout(SafeTimeOutMillisecs)]
    public void Meta_NewTaskCompletes()
    {
      var task = new Task(() => { });
      task.Start();
      Thread.Sleep(10);

      Assert.That(task.IsCompleted, Is.True);
    }

    [Test, Timeout(SafeTimeOutMillisecs)]
    public void Meta_EmptySetOfTasks_WaitsProceed()
    {
      var tasks = new List<Task>();
      Task.WaitAny(tasks.ToArray());
      Task.WaitAll(tasks.ToArray());
    }

    [Test, Timeout(SafeTimeOutMillisecs)]
    public void Meta_EmptySetOfTasks_WaitsGiveExpectedOutputs()
    {
      var tasks = new List<Task>();
      Assert.That(Task.WaitAny(tasks.ToArray(), TimeSpan.FromMilliseconds(100)), Is.EqualTo(-1));
      Assert.That(Task.WaitAll(tasks.ToArray(), TimeSpan.FromMilliseconds(100)), Is.True);
    }

    [Test, Timeout(SafeTimeOutMillisecs)]
    public void Meta_SetOfCompletedTasks_WaitsProceed()
    {
      var tasks = new List<Task>
      {
        new Task(() => { }),
        new Task(() => { })
      };

      tasks.ForEach(task => task.Start());
      Thread.Sleep(10);

      Task.WaitAny(tasks.ToArray());
      Task.WaitAll(tasks.ToArray());
    }

    [Test, Timeout(SafeTimeOutMillisecs)]
    public void Meta_SetOfCompletedTasks_WaitsGiveExpectedOutputs()
    {
      var tasks = new List<Task>
      {
        new Task(() => { }),
        new Task(() => { })
      };

      tasks.ForEach(task => task.Start());
      Thread.Sleep(10);

      Assert.That(Task.WaitAny(tasks.ToArray(), TimeSpan.FromMilliseconds(100)), Is.GreaterThan(-1));
      Assert.That(Task.WaitAll(tasks.ToArray(), TimeSpan.FromMilliseconds(100)), Is.True);
    }

    [Test, Timeout(SafeTimeOutMillisecs)]
    public void Meta_SetOfUnstartedTasks_WaitsDontProceed()
    {
      var tasks = new List<Task>
      {
        new Task(() => { }),
        new Task(() => { })
      };

      Thread.Sleep(10);

      Assert.That(Task.WaitAny(tasks.ToArray(), TimeSpan.FromMilliseconds(100)), Is.EqualTo(-1));
      Assert.That(Task.WaitAll(tasks.ToArray(), TimeSpan.FromMilliseconds(100)), Is.False);
    }


    [Test, Timeout(SafeTimeOutMillisecs)]
    public void Meta_SetOfTasksWithOneCompleted_AnyProceedsAndOutputsIndex()
    {
      var tasks = new List<Task>
      {
        new Task(() => { }),
        new Task(() => { })
      };

      tasks.First().Start();
      Thread.Sleep(10);

      Assert.That(Task.WaitAny(tasks.ToArray()), Is.GreaterThan(-1));
    }

    [Test, Timeout(SafeTimeOutMillisecs)]
    public void Meta_SetOfTasksWithOneCompleted_AllTimesOutWithExpectedOutput()
    {
      var tasks = new List<Task>
      {
        new Task(() => { }),
        new Task(() => { })
      };

      tasks.First().Start();
      Thread.Sleep(10);

      Assert.That(Task.WaitAll(tasks.ToArray(), TimeSpan.FromMilliseconds(100)), Is.False);
    }
  }
}