Почему TaskFactory.Задача StartNew не запускается сразу?

на MSDN документация TaskFactory.StartNew он создает и запускает задачу. Итак, для приведенного ниже примера кода

class Program
{
    public static void Main()
    {
        var t =Task.Factory.StartNew(
                () => SomeLongRunningCalculation(10, Display)
            );
        var t1 = Task.Factory.StartNew(
                () => SomeLongRunningCalculation(20, Display)
            );
        Console.WriteLine("Invoked tasks");
        Task.WaitAll(t, t1);
        Console.ReadLine();
    }

    public static void Display(int value)
    {
        Console.WriteLine(value);
    }

    public static void SomeLongRunningCalculation(int j, Action<int> callBack)
    {
        Console.WriteLine("Invoking calculation for {0}", j);
        System.Threading.Thread.Sleep(1000);
        if (callBack != null)
        {
            callBack(j + 1);
        }
    }
}      

мой ожидаемый результат был

Invoking calculation for 10
Invoking calculation for 20
Invoked tasks
11 
21

но, он отображает

Invoked tasks
Invoking calculation for 20
Invoking calculation for 10
21
11

Я хотел бы узнать

  1. почему задачи не выполняются сразу после StartNew?
  2. что мне делать, чтобы получить результат в ожидаемом формат?

3 ответов


Это очень вероятный результат на машине с одним ядром cpu. Или можно на одном с многоядерным процессором и он тоже занят чем-то другим.

Создание задачи или потока настраивает только логическую структуру операционной системы, которая позволяет запускать код. Планировщик операционной системы делает не немедленно начните его выполнение, если ядра заняты, поток должен конкурировать со всеми другими потоками, которые работают на машине. Типичная для Windows сеанс тысячу или около того. 64 раза в секунду ядро генерирует прерывание, и планировщик повторно оценивает, что происходит, чтобы увидеть, должен ли другой поток получить поворот. Любые потоки, которые не блокируются (ожидая, пока какой-либо другой поток выполнит работу, например, чтение файла или сетевого пакета), имеют право на запуск, и планировщик выбирает тот, с самым высоким приоритетом. Некоторый дополнительный код в планировщике tinkers со значениями приоритета, чтобы гарантировать, что все потоки получат шанс.

шанс-ключевое слово здесь. Планирование потоков -недетерминированный.

обратите внимание, что я никогда ничего не говорил о потоке или задаче или классе ThreadPool. У них нет возможности сделать что-либо о том, как операционная система планирует потоки. Все это можно запретить поток от запуска, задание планировщика пула потоков.

приоритет имеет значение, вы можете возиться с потоком.Приоритет или Задача.Приоритетное свойство влияет на результат. Но нет slamdunk, планировщик операционной системы постоянно настраивает приоритет потоков из базового приоритета, установленного с этим свойством. Вы не можете предотвратить поток от когда-нибудь работает, имея другой с более высоким приоритетом, например.

надеясь, что потоки работают в предсказуемом порядке, вызывает наихудшую ошибку, ошибку гонки потоков. Второе худшее-тупик. Их чрезвычайно трудно отлаживать, потому что они зависят о времени и доступных ресурсах машины и нагрузке. Вы можете только гарантировать, что получите определенный заказ, написав код, который явно позаботится об этом. Что вы делаете, используя примитив потока, такой как мьютекс или замок ключевое слово. Примечательно также, что при попытке добавить такой код в свой фрагмент вы получите программу, которая больше не имеет параллелизма. Или, другими словами, Вы больше не будете использовать задачу. Поток или задача будут полезны, только если вы can позволить ему работать в непредсказуемое время.


почему задачи не выполняются сразу после StartNew?

о MSDN StartNew() будет расписание задание для выполнения.

вызов StartNew функционально эквивалентен созданию задачи с использованием один из его конструкторов, а затем вызов Start, чтобы запланировать его для исполнение. Однако, если создание и планирование не должны быть разделены, StartNew рекомендуется для простоты и спектакль.

поскольку TPL использует потоки ThreadPool-иногда ему приходится выполнять некоторую работу, чтобы зарезервировать и запустить поток ThreadPool для конкретного выполнения задачи. Если вам нужно явно запустить отдельный поток без какого - либо промежуточного механизма, такого как Tpl'S TaskScheduler-создайте и запустите поток вручную, и, очевидно, у вас не будет таких аккуратных вещей, как продолжение.


обратите внимание, как я никогда ничего не говорил о потоке или задаче или ThreadPool класс. У них нет сил что-либо сделать с этим путем. операционная система планирует потоки. Все это можно предотвратить поток от запуска, задание планировщика пула потоков.

войны ... Задачи vs темы... Это зависит от поставленной задачи.

для образца, нам нужно нагрузить 100 изображений от Интернета с 100 одновременными задачи (одна задача для одного клиента) для создания листов карты для 100 клиентов (одна плитка для одного клиента). И у нас есть общий лимит времени, и время загрузки некоторых задач загрузки может перекрывать общий лимит времени. Простой тест показывает, что, одновременное выполнение 100 потоков (Class Thread) за ограниченное время, гораздо более продуктивно, чем выполнение 100 задач (class Task). Тот же результат для 10 потоков против 10 задач. Я имею в виду, что если нам нужно больше, чем "несколько вялых", но надежных задач, то есть сделать гораздо больше работайте в одновременных задачах, тогда мы должны использовать класс Thread.