Search code examples
c#multithreadingtask-parallel-libraryparallel-extensions

How to prioritize nested tasks using a QueuedTaskScheduler?


I'm using the QueuedTaskScheduler from ParallelExtensionsExtras to schedule tasks T, A and B. The QueuedTaskScheduler is set to use only 1 thread. Tasks A and B just sleep for 2000 ms. Task T starts A and B.

I'm using two queues: q1 (priority 1) and q2 (priority 2). From my understanding this means that tasks scheduled on q1 take priority over tasks scheduled on q2. Tasks T and A are started on q1, task B is started on q2. After starting A and B, T waits for them to finish.

I would expect that first A would start and then B, however, the order is completely determined by the order in which A and B are passed to the Task.WaitAll function.

If I do not start A and B as nested tasks (from T), the ordering is as expected.

Why does the QueuedTaskScheduler not work as expected when starting nested tasks?

void Main()
{
    var qts = new QueuedTaskScheduler(TaskScheduler.Default, 1);
    var scheduler1 = qts.ActivateNewQueue(1);
    var scheduler2 = qts.ActivateNewQueue(2);

    Task T = new Task(() =>
    {
        var B = slowTask(scheduler2, "B");
        var A = slowTask(scheduler1, "A");

        Task.WaitAll(A, B);
    });
    T.Start(scheduler1);

    Task.WaitAll(T);
}

private Task slowTask(TaskScheduler scheduler, string name)
{
    var t = new Task(() =>
        {
            Console.WriteLine($"starting {name}");
            Thread.Sleep(2000);
        }, CancellationToken.None, TaskCreationOptions.None);

    t.Start(scheduler);

    return t;
}

Solution

  • I checked the implementation of Task.WaitAll. It will use its own thread (inline) on tasks that have not yet started. It will loop through the task array from back to front, that's why in my case B was started before A. So if you want to make sure that higher priority tasks are always started first, sort the task array that gets passed to Task.WaitAll from low to high priority.

    Note that if the number of available threads is greater than 1, the tasks that are not inlined will be scheduled in expected order (according to priorities).