多线程访问队列的推荐模式......工作线程应该做啥?

Posted

技术标签:

【中文标题】多线程访问队列的推荐模式......工作线程应该做啥?【英文标题】:Recommended pattern for a queue accessed by multiple threads...what should the worker thread do?多线程访问队列的推荐模式......工作线程应该做什么? 【发布时间】:2016-01-19 17:45:56 【问题描述】:

我有一个线程 A 正在添加的对象队列。线程 B 正在从队列中删除对象并处理它们。可能有很多线程 A 和很多线程 B。

当队列被“推送”到时,以及“前端”和“弹出”时,我正在使用互斥锁,如下面的伪代码所示:

线程 A 调用这个来添加到队列中:

void Add(object)

    mutex->lock();
    queue.push(object);
    mutex->unlock();

线程 B 处理队列如下:

object GetNextTargetToWorkOn()

    object = NULL;

    mutex->lock();
    if (! queue.empty())
    
        object = queue.front();
        queue.pop();
    
    mutex->unlock();

    return(object);


void DoTheWork(int param)

    while(true)
    
        object structure;

        while( (object = GetNextTargetToWorkOn()) == NULL)
            boost::thread::sleep(100ms); // sleep a very short time

        // do something with the object
        

困扰我的是while---get object---sleep-if-no-object范式。虽然有要处理的对象,但它很好。但是在线程等待工作的时候有两个问题

a) while 循环正在消耗资源 b) 睡眠意味着浪费时间是一个新的对象进来处理

是否有更好的模式来实现相同的目标?

【问题讨论】:

要使用的“模式”是一个条件变量。 .. 或信号量..... 旁注:不要显式调用lockunlock。 C++11 为您提供lock_guard 用于mutex 锁定的异常/程序员安全RAII 管理,请使用它。 【参考方案1】:

您正在使用旋转等待,更好的设计是使用监视器。阅读更多关于wikipedia 的详细信息。 一个使用 std::condition_variable 的跨平台解决方案和一个很好的例子可以在here 找到。

【讨论】:

是的,监视器模式对我来说效果很好 - 谢谢【参考方案2】:

a) while 循环正在消耗资源 b) 睡眠意味着浪费时间是一个新的对象进来处理

根据我的经验,您使用的睡眠实际上“修复”了这两个问题。

a) 资源消耗是少量 ram,并且非常小可用 cpu 周期的一小部分。

b) 在我开发的操作系统上,睡眠不是浪费时间。

c) 睡眠会影响“反应时间”(又名延迟),但很少成为问题(除了中断)。

在睡眠中花费的时间可能比在这个简单循环中花费的时间长几个数量级。即它并不重要。


恕我直言 - 这是尽快放弃处理器的“好邻居”政策的良好实施。


在我的台式机 AMD64 双核 Ubuntu 15.04 上,信号量强制上下文切换大约需要 13 我们。

100 ms ==> 100,000 us .. 相差 4 个数量级,即非常微不足道。

在我研究过的 5 个操作系统(Linux、vxWorks、OSE 和其他几个嵌入式系统操作系统)中,睡眠(或它们的等价物)是放弃处理器的正确方法,这样它就不会被阻止运行另一个当一个线程处于睡眠状态时线程。


注意:某些操作系统的睡眠可能不会放弃处理器是可行的。因此,您应该始终确认。我还没有找到一个。哦,但我承认我没有在 Windows 上看过/工作太多。

【讨论】:

在 Windows 上,(在我的 i7 上)通过将一个单元发布到线程正在等待的信号量来使线程运行需要 5-8us,(假设核心可用)。 Windows Sleep() API 执行您所期望的 - 从调用线程中删除执行,直到睡眠间隔结束,此时线程再次准备好(然后在内核可用时运行)。虽然 IIRC,发送到等待线程的信号量会为等待线程提供临时优先级提升,但 Sleep() 到期不会。

以上是关于多线程访问队列的推荐模式......工作线程应该做啥?的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程(单例模式堵塞队列定时器)

为啥在python里推荐使用多进程而不是多线程

Okhttp的线程池和高并发

命令模式

访问 linux 线程的本地堆栈(pthreads)

多线程多队列..如何管理它?