使用 C# 的线程池问题

Threadpool issue Using C#

我正在做我的大学项目。主要要求之一是使用 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();
    }

我需要 calculateBeta 的结果来进一步计算。有时线程还没有完成,但程序在没有需要线程提供的数据的情况下继续前进。我现在正在使用 bool 变量,但这个解决方案不是处理它的优雅方法(创建 bool table,检查是否所有线程都已完成)我如何以不同的方式管理它?

// 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");
}

这是因为您正在使用 ThreadPool 来管理您的线程。它会根据很多因素创建一定数量的线程。您可以调整一些设置,但总的来说,当您承诺使用 ThreadPool 来管理您提交给黑盒的线程时。查看 GetMaxThreads and GetMinThreads 和他们的 setter 同行,了解您的一些选择。

在 MSDN 上查看此 ThreadPool Architecture article。它为 class 的方法和原因提供了很好的背景。但是在介绍性段落中你会看到这句话,这是你的难题的关键:

The thread pool is primarily used to reduce the number of application threads and provide management of the worker threads.

如果你想拥有快速连续启动 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

后续代码演示同步线程和"waiting"直到它们全部完成:

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 绑定工作。

使用线程池的想法是,如果您不关心有多少实际线程,而只是想要使您拥有的操作具有高效的吞吐量,从而使框架在如何最好地优化该工作方面有很大的自由度。如果您对拥有的线程数有非常具体的要求,则需要手动创建线程而不是使用线程池。