Я работаю над своим университетским проектом. Одним из основных требований является использование 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, проверка наличия всех потоков) Как я могу управлять этим по-другому?
Это связано с тем, что вы используете 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.");
}
Весь проект пула потоков состоит в том, что ему не нужно создавать новый фактический поток каждый раз, когда новый элемент помещается в очередь. Если пул замечает, что он имеет ожидающие очереди очереди в течение длительного периода времени, он со временем начнет разворачивать новые потоки. Если вы постоянно насыщаете пул потоков операциями, вы увидите, сколько фактических потоков растет. Он также добавит только новые потоки до предела; основанный на том, что он чувствует, будет иметь лучшую пропускную способность. Например, это позволит избежать создания гораздо большего количества потоков, чем ядра, если все потоки активно работают с работой ЦП.
Идея использования пула потоков состоит в том, что вам все равно, сколько фактических потоков существует, а просто хотят иметь эффективную пропускную способность операций, которые у вас есть, что позволяет структуре много свободы в том, как наилучшим образом оптимизировать эту работу. Если у вас есть очень конкретные требования относительно того, сколько потоков у вас есть, вам нужно будет создавать потоки вручную, а не использовать пул.
// 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");
}
while
петли и Thread.Sleep
вызова в yourTask
метод может быть удален. Почему бы не просто вызов Console.Write
или даже комментарий // DO YOUR STUFF
? В методе Button_Click
массив threads
совершенно не нужен. Установка IsBackground
в true
для этого примера не IsBackground
. Вам просто нужно две строки кода, чтобы породить N потоков.
doSth
? Я немного объяснил про пул потоков здесь . Это может помочь.