Проблема Threadpool с использованием C #

1

Я работаю над своим университетским проектом. Одним из основных требований является использование multithreading (пользователь может выбирать номера потоков).

Я новичок в С# и на основе интернет-исследований. Я выбираю ThreadPool.

Я потратил много времени на наблюдение за тем, как потоки действуют с использованием параллельных часов в VS, и я не знаю, как это работает. Например, threadNumber = 10 но параллельные часы показывают только 4 активированные нити.

Вот мой код:

public void calculateBeta()
    {
        var finished = new CountdownEvent(1);
        for (int i = 0; i < threadNumber; i++)
        {
            finished.AddCount();
            ThreadPool.QueueUserWorkItem(
            (state) =>
            {
                try
                {
                   doSth();
                }
                finally
                {
                    finished.Signal();
                }
            });
        }
        finished.Signal();
        finished.Wait();
    }

Что я делаю не так? Я попытался проверить этот код со многими различными значениями числа потоков, и он не работал, как я и искал.

РЕДАКТИРОВАТЬ:

 private void myTask(object index)
    {

            int z = (int)index;
            double[] result = countBeta(createTableB(z), createTableDiagonalA(z));
            int counter = 0;
            if ((rest != 0) && (z == threadNumber - 1))
            {
                for (int j = z * numbersInRow; j < (z + 1) * numbersInRow + rest; j++)
                {
                    N[j] = result[counter];
                    counter++;
                }
            }
            else
            {
                for (int j = z * numbersInRow; j < (z + 1) * numbersInRow; j++)
                {
                    N[j] = result[counter];
                    counter++;
                }
            }
            threads[z] = true;
    }


public void calculateBeta()
    {
        N = new double[num];
        setThreadNumber(2);
        checkThreadNumber();
        setNumberInRow();
        setRest();
        threads = new bool[threadNumber];
        for (int i = 0; i < threadNumber; i++)
        {
            Thread thread = new Thread(this.myTask);
            thread.IsBackground = true;
            thread.Start(i);
        }
        while (!checkThreads())
        {
        }
    }

private bool checkThread()
    {
        bool result = true;
        for (int i = 0; i < threads.Length; i++)
        {
            if (!threads[i])
                result = false;
        }

        return result;
    }

static void Main(string[] args)
    {


        Jacobi jacobi = new Jacobi();
        Console.WriteLine("Metoda Jacobiego");
        Console.WriteLine("Rozwiazywanie ukladu n-rownan z n-niewiadomymi Ax=b");
        jacobi.getNum();
        jacobi.getA();
        jacobi.getB();
        jacobi.calculateBeta();
        jacobi.calculateM();
        jacobi.calculateX();
        jacobi.countNorms();
        Console.ReadLine();
    }

Мне нужны результаты расчетаБета для дальнейших расчетов. Иногда потоки еще не закончены, но программа движется вперед без данных, которые должны предоставляться потоками. Теперь я использую переменную bool, но это решение не является изящным способом справиться с этим (создание таблицы bool, проверка наличия всех потоков) Как я могу управлять этим по-другому?

  • 1
    Пул потоков не будет создавать слишком много потоков.
  • 0
    Сколько времени занимает doSth ? Я немного объяснил про пул потоков здесь . Это может помочь.
Показать ещё 3 комментария
Теги:
multithreading
testing
thread-safety
threadpool

3 ответа

1
Лучший ответ

Это связано с тем, что вы используете ThreadPool для управления вашими потоками. Он создаст определенное количество потоков, основанных на многих факторах. Вы можете настроить некоторые параметры, но по большому счету, когда вы обязуетесь использовать ThreadPool для управления потоками, которые вы передаете в черный ящик. Посмотрите GetMaxThreads и GetMinThreads и их сопоставления для некоторых из ваших вариантов.

Ознакомьтесь с этой статьей архитектуры ThreadPool в MSDN. Это дает хороший фон для ханов и классов класса. Но во вступительном параграфе вы увидите это предложение, которое является ключом к вашей загадке:

Пул потоков в основном используется для уменьшения количества потоков приложений и обеспечения управления рабочими потоками.

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

void ButtonClickHandlerOrSomeOtherMethod()  
{
    for (int i=1; i<=10; i++) // using a 1-based index
    {
        new Thread(ThreadTask).Start(i);
    }
}

void ThreadTask(object i) 
{
    Console.WriteLine("Thread " + i + " ID: " + Thread.CurrentThread.ManagedThreadId);
}

И некоторые результаты выборки:

Thread 1 ID: 19
Thread 2 ID: 34
Thread 3 ID: 26
Thread 4 ID: 5
Thread 5 ID: 36
Thread 6 ID: 18
Thread 7 ID: 9
Thread 8 ID: 38
Thread 9 ID: 39
Thread 10 ID: 40

Последующий код демонстрирует синхронизацию с потоками и "ожидание" до тех пор, пока они не будут закончены:

void ButtonClickHandlerOrSomeOtherMethod() 
{
    // need a collection of threads to call Join after Start(s)
    var threads = new List<Thread>();

    // create threads, add to List and start them
    for (int i=1; i<=10; i++) {
        var thread = new Thread(ThreadTask);
        threads.Add(thread);
        // a background thread will allow main app to exit even
        // if the thread is still running
        thread.IsBackground = true;
        thread.Start(i);
    }

    // call Join on each thread which makes this thread wait on
    // all 10 other threads
    foreach (var thread in threads) 
        thread.Join();

    // this message will not show until all threads are finished
    Console.WriteLine("All threads finished.");
}

void ThreadTask(object i) 
{
    Console.WriteLine("Thread " + i + " ID: " + Thread.CurrentThread.ManagedThreadId);
    // introducing some randomness to how long a task "works on something"
    Thread.Sleep(100 * new Random().Next(0, 10));
    Console.WriteLine("Thread " + i + " finished.");
}
  • 0
    Большое спасибо за помощь, все отлично работает. Я тестировал темы некоторое время, и у меня есть один вопрос. Результаты очень запутанные, я понятия не имею, что случилось. Во-первых, моя программа без многопоточности выполняет вычисления быстрее, чем версия с многопоточностью (тестируется с большим количеством входных данных). Кроме того, время вычисления каждый раз для одних и тех же данных различно (38 мс, 60 мс, 96 мс) 1 мс для версии без темы. Количество использованных нитей составило 4,5,6. Я думаю, это не нормально, это не имеет никакого смысла и логики. Что вы думаете об этом?
  • 0
    @kobasek - Да, на все это можно ответить. Это распространенное заблуждение, что многопоточность означает быстрее. Это редко правда на самом деле. Существуют накладные расходы на создание потоков, и если алгоритм не использует потоки очень хорошо, он не будет быстрее. Различия во времени вычислений можно объяснить различиями в активности вашего компьютера во времени. Программы совместно используют ресурсы компьютера, и иногда, в зависимости от времени, потоку может потребоваться время процессора. Когда вы проводите тестирование производительности, вы должны выполнить много тестов и получить среднее значение.
1

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

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

1
// Array of threads launched. 
// This array is useful to trace threads status.
Thread[] threads;

private void myTask(object index)
{
    Console.Write("myTask {0} started\n", index);
    Console.Write("myTask {0} finisced\n", index);
}

public void calculateBeta(UInt16 threadNumber)
{
    // Allocate a new array with size of requested number of threads
    threads = new Thread[threadNumber];

    // For each thread
    for (int i = 0; i < threadNumber; i++)
    {
        // Thread creation
        threads[i] = new Thread(this.myTask);

        // IsBackground set to true grants that the allication can be "killed" without wait for all threads termination
        // This is useful in debug to be sure that an error in task doesn't freeze the app.
        // Leave it to false in release
        #if DEBUG
        threads[i].IsBackground = true;
        #endif

        // Start the thread
        threads[i].Start(i);
    }

    // Waits until all threads complete.
    while (!checkThreads());
}

private bool checkThreads()
{
    bool result = true;
    for (int i = 0; i < threads.Length; i++)
    {
        // If the thread wasn't disposed
        if (threads[i] != null)
        {
            // Check if the thead is alive (means is working)
            if (threads[i].IsAlive == true) 
            {
                result = false;
            }
            else // The thread is not working 
            {
                // Dispose the thread
                threads[i].Join();
                // Set pointer to null to signal that the task was 
                threads[i] = null;
            }
        }
    }

    return result;
}


private void Button_Click(object sender, RoutedEventArgs e)
{
    Console.Write("Starting tasks!!\n");

    calculateBeta(10);

    Console.Write("All tasks finished!!\n");
}
  • 0
    Как передать переменную в метод yourTask?
  • 0
    С некоторыми модификациями этот код будет работать, но есть тонна кода, который не нужен для демонстрации концепции. Этот дополнительный код становится бесполезным, и пять строк кода - это все, что вам действительно нужно. И в while петли и Thread.Sleep вызова в yourTask метод может быть удален. Почему бы не просто вызов Console.Write или даже комментарий // DO YOUR STUFF ? В методе Button_Click массив threads совершенно не нужен. Установка IsBackground в true для этого примера не IsBackground . Вам просто нужно две строки кода, чтобы породить N потоков.
Показать ещё 10 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню