【发布时间】:2015-10-18 01:57:27
【问题描述】:
我有一个生产者线程和一个消费者线程,生产者是实时的并且对确定性敏感。
因此,我决定使用无锁 fifo 队列将处理从生产者线程提升到消费者线程。目标是让消费者既响应又避免忙等待,同时永远不会延迟生产者不确定的时间;因此任何分配/锁定(和内核条目,我想?)等都是完全不可能的。
我已经实现了这种模式,它似乎运行良好,但是我不确定为什么需要互斥锁:
std::mutex m;
std::condition_variable cv;
void consumer()
{
std::unique_lock<std::mutex> lock(m);
while (1)
{
cv.wait(lock);
// process consumation...
}
}
void producer()
{
while (1)
{
// produce and post..
cv.notify_one();
}
}
其他规范示例似乎也将互斥锁锁定在生产者中,为什么?我的数据通信已经是线程安全的,所以不需要它。另外,这是否容易丢失信号?
在研究这个时,我偶然发现了似乎明确用于这种情况的信号量。与这个系统相比有什么好处?我目前更喜欢我的解决方案,因为它是标准库的一部分。
【问题讨论】:
-
与丢失信号完全相反,这也容易受到虚假唤醒的影响。不要忘记,条件变量不是“状态”;它们只是通信状态已更改的信号机制。状态在 you 提供的单独数据存储变量中进行管理,最重要的是,you 使用 mutex-cv 配对的互斥锁进行保护。该检查以及对状态变量的任何修改,仅在互斥体锁定时进行。
-
我考虑了虚假唤醒,但在这种情况下它们没有问题,因为除非有东西存在,否则消费者本身永远不会成功弹出队列,因此它将直接再次进入睡眠状态(?)。我可以添加一个等待检查,但我认为它是多余的。如前所述,作为队列的 state 是并发线程安全的,所以我不明白为什么需要使用互斥锁来保护它。但是你措辞的方式,你似乎说这很容易丢失信号。怎么会这样?
-
当
notify_one发生并且cvar 上没有活跃的服务员时,我提到的错过的信号(我相信你在质疑)发生。由于您没有提供谓词,因此唯一可以打破该等待的是通知到达某人正在等待。否则会丢失。 see trivial example. -
好的,我现在看到了。我想我以某种方式认为信号可能是“未决”的,这样接收信号的唤醒线程就不会再次进入睡眠状态(那一回合)。所以除非我在生产者中使用锁,否则我基本上不能这样做?
-
我认为你可以在你的情况下这样做,因为你的队列,正如你所说,是无锁的,只需添加一个谓词来检查你的无锁队列等待,当然要确保消费者拥有进入等待之前的互斥锁。您的情况与传统的 cv-mtx 工作略有不同,因为该队列是无锁的。在您的情况下,生产者在附加队列之前不需要锁定互斥锁,但在发出
cv.notify_one()之前,他附加到队列之前 是关键。如果您启动多个消费者,则需要notify_all。
标签: c++ multithreading synchronization producer-consumer