Я поиграл с задачами .Net, получив следующий код:
public static async Task TaskSchedulerBehaviour()
{
var topLevelTasks = Enumerable.Range(0, 5).Select(async n =>
{
await Task.Delay(50); // THIS LINE MAKES THE DIFFERENCE
var steps = Enumerable.Range(0, 100000);
foreach (var batch in steps.Batch(1000)) { /* ".Batch" is contained in MoreLinq */
await Task.WhenAll(batch.Select(async step => await WorkStep(n, step)));
}
});
await Task.WhenAll(topLevelTasks);
async Task WorkStep(int worker, int step)
{
if (step % 100 == 0) {
Console.WriteLine($"worker={worker}, step={step}");
}
await Task.Delay(10);
}
}
Показанный код содержит несколько «больших» задач верхнего уровня, которые выполняют большую работу (= много небольших задач (WorkStep
); которые вызывают только Task.Delay
).
Одна строка в коде помечена комментарием: если эта строка удалена, может случиться так, что некоторые задачи верхнего уровня будут поставлены в очередь, пока все остальные не будут выполнены. Кажется, они могут голодать, если другие задачи «верхнего уровня» очень и очень интенсивны.
С другой стороны, если я добавлю закомментированную строку, поведение будет намного лучше: кажется, что все задачи верхнего уровня получают примерно одинаковое количество времени для выполнения своих дочерних задач. Они работают одновременно.
Почему это происходит? Разве планировщик задач не является простой FIFO-очередью или чем-то вроде этого?
Большое спасибо