使用 C# 的线程池问题
本文关键字:问题 线程 使用 | 更新日期: 2023-09-27 18:37:18
我正在做我的大学项目。主要要求之一是使用multithreading
(用户可以选择线程编号)。
我是 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();
}
我做错了什么?我尝试使用许多不同的线程数值测试此代码,但它没有像我寻找的那样工作。
编辑:
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();
}
我需要从计算 Beta 到进一步计算的结果。有时线程尚未完成,但程序在没有需要由线程提供的数据的情况下继续前进。我现在正在使用布尔变量,但这个解决方案不是一种优雅的处理方式(创建布尔表,检查是否所有线程都被 fnished)我如何以不同的方式管理它?
这是因为您正在使用 ThreadPool 来管理您的线程。它将根据许多因素创建一定数量的线程。您可以调整一些设置,但总的来说,当您承诺使用 ThreadPool 来管理您的线程时,您会提交到黑盒。查看 GetMaxThreads 和 GetMinThreads 及其资源库对应项,了解您的一些选项。
查看这篇关于 MSDN 的 ThreadPool 体系结构文章。它为课程的方式和原因提供了良好的背景。但是在介绍性段落中,您将看到这句话,这是您难题的关键:
线程池主要用于减少应用程序数量 线程,并提供工作线程的管理。
如果你想拥有那种快速连续启动 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);
}
和一些示例输出:
线程 1 ID: 19线程 2 ID: 34线程 3 ID: 26线程 4 ID: 5线程 5 ID: 36线程 6 ID: 18线程 7 ID: 9线程 8 ID: 38线程 9 ID: 39线程 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.");
}
线程池的整个设计是,它不必在每次新项目排队时都创建新的实际线程。 如果池注意到队列中有项目长时间处于挂起状态,则随着时间的推移,它最终将开始启动新线程。 如果您不断用操作使线程池饱和,您将看到实际线程数增加。 它也只会添加新线程,直到限制;根据它的感觉将具有最佳吞吐量。 例如,它将避免创建比内核多得多的线程,假设所有线程都在主动运行 CPU 绑定工作。
使用线程池的想法是,如果您不关心实际线程的数量,而只是希望拥有您拥有的操作的有效吞吐量,从而允许框架在如何最好地优化该工作方面有很大的自由度。 如果您对拥有的线程数有非常具体的要求,则需要手动创建线程,而不是使用池。
// 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");
}