具有多个生产者的高效消费者线程 [英] Efficient consumer thread with multiple producers

查看:93
本文介绍了具有多个生产者的高效消费者线程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图通过在必要时跳过昂贵的事件操作来使生产者/消费者线程状况更有效,例如:

I am trying to make a producer/consumer thread situation more efficient by skipping expensive event operations if necessary with something like:

//cas(variable, compare, set) is atomic compare and swap
//queue is already lock free

running = false


// dd item to queue – producer thread(s)

if(cas(running, false, true))
{
  // We effectively obtained a lock on signalling the event
  add_to_queue()
  signal_event()
}
else
{
  // Most of the time if things are busy we should not be signalling the event
  add_to_queue()

  if(cas(running, false, true))
    signal_event()
}

...

// Process queue, single consumer thread

reset_event()

while(1)
{
  wait_for_auto_reset_event() // Preferably IOCP

  for(int i = 0; i &lt SpinCount; ++i)
    process_queue()

  cas(running, true, false)

  if(queue_not_empty())
    if(cas(running, false, true))
      signal_event()
}

显然,要使这些事情正确无误,所以上面的伪代码正确吗?可以用一种解决方案向事件发出比实际需要更多的信号,但没有一种解决方案可以针对每个项目发出信号.

Obviously trying to get these things correct is a little tricky(!) so is the above pseudo code correct? A solution that signals the event more than is exactly needed is ok but not one that does so for every item.

推荐答案

这属于停止混乱并重新开始工作"子类别,称为过早优化". :-)

This falls into the sub-category of "stop messing about and go back to work" known as "premature optimisation". :-)

如果昂贵的"事件操作占用了大量时间,则您的设计是错误的,而不是使用生产者/消费者,您应该使用关键部分/互斥体,并从调用线程中进行工作.

If the "expensive" event operations are taking up a significant portion of time, your design is wrong, and rather than use a producer/consumer you should use a critical section/mutex and just do the work from the calling thread.

如果您真的很担心,我建议您对应用程序进行概要分析.

I suggest you profile your application if you are really concerned.

已更新:

正确答案:

制作人

ProducerAddToQueue(pQueue,pItem){

    EnterCriticalSection(pQueue->pCritSec)
        if(IsQueueEmpty(pQueue)){
            SignalEvent(pQueue->hEvent)
        }

        AddToQueue(pQueue, pItem)
    LeaveCriticalSection(pQueue->pCritSec)
}

消费者

nCheckQuitInterval = 100; // Every 100 ms consumer checks if it should quit.

ConsumerRun(pQueue)
{
    while(!ShouldQuit())
    {
        Item* pCurrentItem = NULL;
        EnterCriticalSection(pQueue-pCritSec);
            if(IsQueueEmpty(pQueue))
            {
                ResetEvent(pQueue->hEvent)
            }
            else
            {
                pCurrentItem = RemoveFromQueue(pQueue);
            }
        LeaveCriticalSection(pQueue->pCritSec);

        if(pCurrentItem){
            ProcessItem(pCurrentItem);
            pCurrentItem = NULL;
        }
        else
        {
            // Wait for items to be added.
            WaitForSingleObject(pQueue->hEvent, nCheckQuitInterval);
        }

    }
}

注意:

  • 该事件是手动重置事件.
  • 关键部分保护的操作非常快捷.仅当队列转换为空状态或从空状态转换时,才设置或重置事件.必须在关键区域内设置/重置它,以避免出现竞争情况.
  • 这意味着关键部分仅保留了很短的时间.这样竞争就很少了.
  • 除非有争议,否则关键部分不会阻止.因此,上下文切换将很少见.

假设:

  • 这是一个真正的问题,而不是家庭作业.
  • 生产者和消费者将大部分时间都花在做其他事情上,即将物品准备好放入队列,在将它们从队列中删除后进行处理.
  • 如果他们大部分时间都在执行实际的队列操作,则不应使用队列.我希望这很明显.

这篇关于具有多个生产者的高效消费者线程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆