使工作线程等待任务的 CPU 效率最高的方法是啥?

Posted

技术标签:

【中文标题】使工作线程等待任务的 CPU 效率最高的方法是啥?【英文标题】:What is most CPU efficient method to make the worker threads wait for tasks?使工作线程等待任务的 CPU 效率最高的方法是什么? 【发布时间】:2011-01-04 23:30:57 【问题描述】:

在我当前的 C#/NET 3.5 应用程序中,我有一个任务队列(线程安全),我有 5 个工作线程,它们必须不断地在队列中寻找任务。如果有任务可用,任何一个工作人员都会将该任务出列并采取必要的行动。

我的工作线程类如下:

public class WorkerThread

    //ConcurrentQueue is my implementation of thread safe queue
    //Essentially just a wrapper around Queue<T> with synchronization locks
    readonly ConcurrentQueue<CheckPrimeTask> mQ; 
    readonly Thread mWorker;
    bool mStop;

    public WorkerThread (ConcurrentQueue<CheckPrimeTask> aQ) 
        mQ = aQ;
        mWorker = new Thread (Work) IsBackground = true;
        mStop = false;
    

    private void Work () 
        while (!mStop) 
            if (mQ.Count == 0) 
                Thread.Sleep (0);
                continue;
            

            var task = mQ.Dequeue ();
            //Someone else might have been lucky in stealing
            //the task by the time we dequeued it!!
            if (task == null) 
                continue;

            task.IsPrime = IsPrime (task.Number);
            task.ExecutedBy = Thread.CurrentThread.ManagedThreadId;
            //Ask the threadpool to execute the task callback to 
            //notify completion
            ThreadPool.QueueUserWorkItem (task.CallBack, task);
        
    

    private bool IsPrime (int number) 
        int limit = Convert.ToInt32 (Math.Sqrt (number));
        for (int i = 2; i <= limit; i++) 
            if (number % i == 0)
                return false;
        

        return true;
    

    public void Start () 
        mStop = false;
        mWorker.Start ();
    

    public void Stop () 
        mStop = true;
    

问题是当队列为空时,它会消耗过多的 CPU(接近 98%)。我尝试了 AutoResetEvent 来通知工作人员队列已更改。因此,他们有效地等待该信号设置。它已将 CPU 降低到接近 0%,但我不完全确定这是否是最好的方法。您能否建议一种更好的方法来保持线程空闲而不损害 CPU 使用率?

【问题讨论】:

使用重置事件将是最好的方法。 另一种可能是使用 ThreadPool.QueueUserWorkItem 还有一个是使用计时器来检查队列并触发工作线程。 【参考方案1】:

查看BlockingQueue 的这个实现。如果队列为空,它使用 Monitor.Wait() 使线程进入睡眠状态。添加项目时,它使用 Monitor.Pulse() 唤醒在空队列上休眠的线程。

另一种技术是使用semaphore。每次将项目添加到队列时,调用 Release()。当您需要队列中的项目时,请调用 WaitOne()。

【讨论】:

我喜欢 Semaphore 的想法,因为它确保如果将一项添加到队列中,则仅唤醒一个线程。使用重置事件唤醒所有工作线程。谢谢。 但是它有一个downsite,我必须在我创建信号量时指定队列中允许的最大项目数!信号量的效率是否足以处理非常大的计数? 回应第一条评论。 Monitor.Pulse 只唤醒一个线程(Monitor.PulseAll)会唤醒每个线程,所以没有区别。 只要设置最大可能的值。 new Semaphore(0, Int32.MaxValue) 郑重声明:我使用了信号量技术,效果很好!谢谢大家。【参考方案2】:

您当前的工作方法中有Thread.Sleep(0),用于没有队列项。将其更改为 anything 大于 0,您的 CPU 使用率就会下降。尝试10 开头...

【讨论】:

【参考方案3】:

你有几个我能想到的选择。

一种方法是在循环期间放置一个小线程睡眠。这基本上会将您的 CPU 使用率降至 0,这是相当标准的做法。

另一种方法是按照 Mitch Wheat 在 cmets 中的建议使用重置(自动或手动)。

您还可以设计某种 IdleTask,让线程休眠一段时间,如果您的队列为空,只需处理 IdleTask(它将线程休眠)。

【讨论】:

【参考方案4】:

如果您的队列是线程安全的,那么您就不需要这样做...

    //Someone else might have been lucky in stealing 
    //the task by the time we dequeued it!! 
    if (task == null)  
        continue; 

【讨论】:

以上是关于使工作线程等待任务的 CPU 效率最高的方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

同步和异步的区别是啥

浅析java多线程

Java笔记:多线程

并发技术进程线程和锁拾遗

Windows 线程等待方法

线程的通信(生产者消费者问题)