Запуск задачи, когда количество активных потоков в ThreadPool больше чем ThreadPool.GetMinThreads () - PullRequest
0 голосов
/ 04 декабря 2018

Взято из ответа на один из моих предыдущих вопросов ( Task.Factory.StartNew начинается с большой задержкой, несмотря на наличие доступных потоков в пуле потоков ):

"Это не такзначение рабочих потоков MAX, на которое нужно обратить внимание - это значение MIN, которое вы получаете через ThreadPool.GetMinThreads (). Максимальное значение - это абсолютный максимальный поток, который может быть активным. Минимальное значение - это число, которое всегда остается активным.попробуйте запустить поток, когда число активных потоков меньше максимального (и больше минимального), вы увидите задержку в 2 секунды. "

Итак, я подготовил пример кода дляпроверьте это:

ThreadPool.GetMinThreads() возвращает "8" для моей машины, и я запускаю код на своей локальной машине.

Мой код выглядит следующим образом:

        Task task = null;

        int workerThreads = 0;
        int completionPortThreads = 0;



        ThreadPool.GetMinThreads(out workerThreads, out completionPortThreads);
        Logger.WriteInfo(LogCode.EMPTY_INFO,  workerThreads.ToString());**returns "8"**

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread1");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread1");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread2");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread2");
            while (true)
            {
                DoSomthing();
            }
        });
    ;
        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread3");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread3");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread4");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread4");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread5");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread5");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread6");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread6");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread7");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread7");
            while (true)
            {
                DoSomthing();
            }
        });


        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread8");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread8");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread9");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread9");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread10");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread10");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread11");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread11");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread12");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread12");
            while (true)
            {
                DoSomthing();
            }
        });

        Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread13");
        task = Task.Factory.StartNew(() =>
        {
            Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread13");
            while (true)
            {
                DoSomthing();
            }
        });


private void DoSomthing()
    {
        int j = 1;
        for (int i = 0; i < 2000; i++)
        {
            j = i * i;
        }
    }

Класс Logger просто использует log4net.dll.Итак, ThreadPool.GetMinThreads () возвращает 8 для моей машины. Минимальное значение - это число, которое всегда остается активным.Если вы попытаетесь запустить поток, когда число активных потоков меньше максимального (и больше минимального), вы увидите задержку в 2 секунды.

Итак, для потока номер 9 и т. Д.:

Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread9");
    task = Task.Factory.StartNew(() =>
    {
        Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread9");
        while (true)
        {
            DoSomthing();
        }
    });

Я ожидаю, что будет задержка в 2 секунды между

Logger.WriteInfo(LogCode.EMPTY_INFO, "before thread9");

и

 Logger.WriteInfo(LogCode.EMPTY_INFO, "after thread9");

, потому что я пытаюсь запустить поток (из потокапул), когда количество активных потоков больше, чем мин (больше, чем 8).Фактический результат заключается в том, что для всего потока 9–13 существует задержка в несколько маленьких миллисекунд (менее полсекунды).

Почему задержка не равна 2 секундам?Я имею в виду, что все активные потоки в моем пуле потоков заняты, и для потока 9 необходимо выделить другой поток, поэтому должна быть задержка.

После написания этого примера приложения я неуверен в сценарии в моем предыдущем вопросе.

Ответы [ 2 ]

0 голосов
/ 04 декабря 2018

Что ж, изучая документацию MSDN для ThreadPool, я не нашел ничего связанного с задержкой в ​​2 секунды при достижении значения, превышающего минимальное значение, и я думаю, что это не точный ответ, поскольку задержка зависит отДля многих факторов, связанных с аппаратным обеспечением и ОС, указывается следующее:

По умолчанию минимальное количество потоков устанавливается равным количеству процессоров в системе.При достижении минимума пул потоков может создавать дополнительные потоки в этой категории или ждать завершения некоторых задач.Начиная с .NET Framework 4, пул потоков создает и уничтожает потоки для оптимизации пропускной способности, которая определяется как количество задач, выполняемых за единицу времени.Слишком мало потоков может не оптимально использовать доступные ресурсы, тогда как слишком много потоков может увеличить конфликт ресурсов.

А документация для GetMinThreads выглядит следующим образом:

Извлекает минимальное количество потоков, создаваемых пулом потоков по требованию, по мере выполнения новых запросов, перед переключением на алгоритм управления созданием и уничтожением потоков

Идокументация не содержит более подробной информации об этом Алгоритме переключения , так каков источник этой 2-секундной задержки, это от тестирования на его машине?

Однако, если вам нужна дополнительная информация оВ этом случае .NET Framework Source общедоступен, вы можете подробно проверить алгоритм, но также вы не получите статическое число для задержки из-за аппаратных факторов и факторов зависимости от ОС.

0 голосов
/ 04 декабря 2018

Здесь есть несколько проблем.

  • Сначала , вы используете Task.Factory.StartNew, что, вероятно, не должно быть, в большинстве случаев вывероятно, следует использовать более современные Task.Run, на которых написано множество вопросов и блогов.

  • Во-вторых, , вы цитируете вопрос, который цитирует документацию, которая старше текущей структуры.Документация изменилась.Раньше он предусматривал миллисекундную задержку при создании потоков.

  • В-третьих , задачи не являются потоками.

В моем понимании это планировщик задач (в зависимости от того, что выuse) Использует эвристику, чтобы определить, хочет ли он дать вам поток или нет в каждой категории, и нет произвольной задержки в миллисекундах.

То, что документация говорит в настоящее время:

Класс ThreadPool

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

Дело в том, что если выВы полагаетесь на типичное поведение планировщика задач, чтобы разделить потоки в определенном темпе, тогда вы наверняка делаете что-то не так.Это деталь реализации и может меняться от версии к версии.В лучшем случае вы можете увеличить минимальное количество потоков, но задача планировщиков задач состоит в том, чтобы отвлечь вас от этого уровня детализации в значительной степени.Он создан для того, чтобы сделать для вас лучшее.

Если вам нужно определенное количество потоков, либо создайте свой собственный планировщик задач, либо создайте свои собственные потоки и пропустите планировщик задач все вместе

...