Есть ли лучший способ реализовать многопоточность? - PullRequest
0 голосов
/ 05 июля 2019

Приведенный ниже фрагмент исходного кода увеличивает потребление ресурсов процессора! Есть ли лучший способ реализовать многопоточность?

// This application will run 24 hours 7 days per week
//     in server

static void Main(string[] args)
{
    const int NUM = 10;

    Thread[] t = new Thread[NUM];

    for (int i = 0; i < t.Length; i++)
        t[i] = new Thread(new ThreadStart(DoWork));

    foreach (Thread u in t)
        u.Start();
}

static void DoWork()
{
    while (true)
    {
        // Perform scanning work
        // If some conditions are satisfied,
        //     it will perform some actions
    }
}

1 Ответ

1 голос
/ 05 июля 2019

На вопрос трудно ответить, если неясно, что находится внутри вращающейся петли while(true). Если вы запустите 10 потоков, каждый из которых будет вращаться без каких-либо ожиданий (используя что-то вроде AutoResetEvent.WaitOne(), Thread.Sleep() и т. Д.), ЦП будет израсходован - поскольку вы запрашиваете его у ЦП.

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

Концептуально поток должен работать таким образом.

while(true)
{
    // Wait until a thread has something meaningful to do. Waiting can be done for instance by calling AutoResetEvent.WaitOne().

    // Do a meaningful work here.
}

Если вы выполняете свой код потока таким образом, когда поток ожидает, он не тратит процессор, поэтому система не перегружена, и другие потоки / процессы могут выполнять свою работу.

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

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

Если, скажем, вам нужно проверять только каждую секунду, выполняются ли условия, добавьте вызовы Thread.Sleep(1000) в ваш код потока. Это значительно увеличивает общую производительность. Даже Thread.Sleep(0) намного лучше, чем вращение без ожидания.

Одно важное замечание здесь. В современном C # потоки не должны использоваться в качестве основного механизма асинхронного программирования. Использование асинхронного программирования на основе задач с использованием class Task реализовать гораздо проще, особенно если вы используете ключевые слова await-async C # и в большинстве случаев приводит к повышению производительности.

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

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

Здесь - пример асинхронного программирования на основе задач.

...