2015-01-13 6 views
1

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

Я новичок в C# и на основе интернет-исследований. Я выбираю 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(); 
    } 

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

EDIT:

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(); 
    } 

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

+1

Пул потоков не будет создавать слишком много потоков. – SLaks

+0

Как долго длится 'doSth'? Я немного объяснил о threadpool [здесь] (http://stackoverflow.com/a/26141323/2530848). Это может помочь. –

+3

Это уже очень хорошо покрыто. Просто google "как работает .net threadpool?" и начните читать сверху. –

ответ

1

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

Отъезд ThreadPool Architecture article на 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. Я думаю, это не нормально, это не имеет никакого смысла и логики. Что вы думаете об этом? – sheddar

+0

@kobasek - Да, на все это можно ответить. Это распространенное заблуждение, что многопоточность означает быстрее. На самом деле это редко бывает. Для создания потоков есть накладные расходы, и если алгоритм не использует потоки очень хорошо, это не будет быстрее. Различия во времени вычислений можно объяснить различиями в активности вашего компьютера с течением времени. Программы совместно используют ресурсы компьютера, и иногда, в зависимости от времени, потоку может потребоваться время процессора. Когда вы выполняете тестирование производительности, вы должны запускать множество тестов и принимать среднее значение. –

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? – sheddar

+0

С некоторыми изменениями этот код будет работать, но есть тонна кода, которая не нужна для демонстрации концепции. Этот дополнительный код просто становится крутым, и пять строк кода - это все, что вам действительно нужно. Как цикл while, так и 'Thread.Sleep' в методе' yourTask' могут быть удалены. Почему не просто простой вызов Console.Write или даже просто комментарий '// DO YOUR STUFF'? В методе 'Button_Click' массив' threads' совершенно не нужен. Для этого примера установка 'IsBackground' в' true' не требуется. Вам просто нужны две строки кода, чтобы создать N количество потоков. –

+0

@Paul Во-первых, массив был обязательным в моем решении, если задача имеет некоторое время. Как вы могли прекратить/остановить его без ссылки для каждой отдельной задачи? Во-вторых, где, в вопросе, написано, что задачи должны быть простыми и должны заканчиваться в конце их кода? В-третьих: для примера для новичков, я думаю, лучше установить IsBackground в true, чтобы не допустить, чтобы тестовое приложение блокировалось. MSDN писал: ** Фоновые потоки идентичны потокам переднего плана, за исключением того, что фоновые потоки не препятствуют завершению процесса **. – LPs

1

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

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

Смежные вопросы