Подтвердить что ты не робот

Множественные задачи замедляются

Код:

static void DoIt(string name)
{
    Console.WriteLine("Hello {0} | {1}", name, Thread.CurrentThread.ManagedThreadID);
    Thread.Sleep(5000);
    Console.WriteLine("Bye {0} | {1}", name, Thread.CurrentThread.ManagedThreadID);
}

static void Main()
{
    Task.Factory.StartNew(() => DoIt("One"));
    Task.Factory.StartNew(() => DoIt("Two"));
    Task.Factory.StartNew(() => DoIt("Three"));
    Task.Factory.StartNew(() => DoIt("Four"));
    Task.Factory.StartNew(() => DoIt("Five"));
    Task.Factory.StartNew(() => DoIt("Six"));
    Task.Factory.StartNew(() => DoIt("Seven"));
    Task.Factory.StartNew(() => DoIt("Eight"));
    Task.Factory.StartNew(() => DoIt("Nine"));
    Task.Factory.StartNew(() => DoIt("Ten"));

    Console.ReadKey();
}

Как получилось, что он может сразу запустить первые 3 Задачи, но затем требуется 5-10 секунд для запуска Задачи 4, и после запуска Задачи 4 это займет 5-10 сек до начала Задачи 5 и так далее. Это что-то делает? Может кто-нибудь прояснить, что происходит?

4b9b3361

Ответ 1

Как получилось, что он может сразу запустить первые 3 Задачи, но затем требуется 5-10 секунд для запуска Задачи 4, и после запуска Задачи 4 это займет 5-10 сек до начала Задачи 5 и так далее. Это что-то делает? Может кто-нибудь прояснить, что происходит?

По умолчанию при первом запуске этот параметр ThreadPool выделяется с использованием минимального количества рабочих потоков. После того, как запланированы первые 4 задания, поток будет "наращивать", чтобы обрабатывать больше со временем, поэтому вы видите задержку.

В моей системе (которая имеет 8 ядер) первые 8 являются мгновенными, затем следующие два запускаются через секунду.

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

Попробуйте следующее, чтобы увидеть это поведение в действии. Если вы оставите вызов SetMinThreads на месте, все они будут немедленно назначены. Если вы прокомментируете это, вы увидите, что в первый раз это займет некоторое время, но во второй раз (если вы дождитесь завершения потоков) потоки будут выполняться немедленно.

static void DoIt(string name)
{
    Console.WriteLine("Hello {0} | {1} - {2}", name, Thread.CurrentThread.ManagedThreadId, DateTime.Now);
    Thread.Sleep(5000);
    Console.WriteLine("Bye {0} | {1} - {2}", name, Thread.CurrentThread.ManagedThreadId, DateTime.Now);
}

static void Main()
{
    int workerThreads, complete;
    ThreadPool.GetMinThreads(out workerThreads, out complete);

    Console.WriteLine(workerThreads);

    // Comment out this line to see the difference...
    // WIth this commented out, the second iteration will be immediate
    ThreadPool.SetMinThreads(100, complete);

    Action run = () =>
        {
            for (int i = 0; i < 20; ++i)
            {
                int tmp = i;
                Task.Factory.StartNew(() => DoIt(tmp.ToString()));
            }
        };

    run();
    Console.WriteLine("Press a key to run again...");
    Console.ReadKey();

    run();

    Console.WriteLine("Press a key to exit...");
    Console.ReadKey();
}

Обратите внимание, что это поведение на самом деле мало связано с TPL в целом - это больше используется по умолчанию TaskScheduler, который просто передает задачи на ThreadPool. Если вы должны были установить эти потоки с помощью подсказки LongRunning в вашем вызове StartNew(), например, все они будут запускаться немедленно (поскольку планировщик по умолчанию настроит новый выделенный поток и выполнит его немедленно).

Ответ 2

Задачи не замедляются, они находятся в очереди в параллельной библиотеке задач. CLR знает, сколько логических ядер доступно на вашем компьютере; TPL пул потоков использует эту информацию, чтобы определить, сколько рабочих потоков будет доступно. В вашем случае у вас, вероятно, есть четыре логических ядра; отнимая один для основного потока (на котором выполняется Main()), три ядра (и три сотрудника пула потоков) остаются для выполнения задач.

Ответ 3

TPL содержит очень сложные алгоритмы, которые адаптируются к рабочей нагрузке. То, что вы испытываете, касается прав на 4-ядерную машину - TPL решила сделать то, что, по его мнению, выгодно в ситуации, когда создано столько потоков.