我有一个使用std::atomics
实现的无锁单生成器多个使用者队列,其方式类似于Herb Sutters CPPCon2014 talk。
有时候,生产者太慢而无法养活所有消费者,因此消费者可能会饿死。我想阻止饥饿的消费者敲击队列,因此我为10ms
添加了一个睡眠。该值是任意的,不是最佳的。我想使用一个消息,一旦队列中有一个空闲插槽,消费者就可以发送给生产者。在基于锁的实现中,我自然会使用std::condition_variable
来执行此任务。但是现在在我的无锁实现中,我不确定,如果引入mutex
是正确的设计选择,只能使用std::condition_variable
。
我只是想问你,在这种情况下mutex
是否是正确的方法?
编辑:我有一个生产者,从不睡觉。并且有多个消费者,如果他们饿死就会入睡。因此整个系统总是在取得进展,因此我认为它是无锁的。
我目前的解决方案是在消费者GetData功能中执行此操作:
std::unique_lock<std::mutex> lk(_idleMutex);
_readSetAvailableCV.wait(lk);
一旦新数据准备好,这就在制作人的线程中:
_readSetAvailableCV.notify_all();
答案 0 :(得分:2)
如果你的大多数线程只是等待让生产者将资源排入队列,我不确定无锁实现是否值得值得。大多数情况下,你的线程会睡觉,他们不会为了队列锁而互相争斗。
这就是为什么我认为(根据你提供的数据量),改变所有使用互斥锁+ conditional_variable的方法就好了。当生产者将资源排入队列时,它只通知一个线程(使用notify_one()
)并释放队列锁。锁定队列的使用者将资源出列,并在队列再次为空时返回休眠状态。线程之间不应该有任何真正的“摩擦”(如果你的制作人很慢),所以我会选择它。
答案 1 :(得分:2)
我刚观看了有关并发TS的CPPCON视频: Artur Laksberg @cppcon2015
在这次演讲的中间,Artur解释了我的问题究竟如何通过障碍和锁存来解决。他还以我的方式使用condition_variable显示现有的解决方法。他强调了一些关于用于此目的的condition_variable的弱点,比如在你进入等待之前的虚假唤醒和丢失通知信号。 但是在我的应用程序中,这些限制没有问题,所以我想现在,我将使用我在编辑帖子中提到的解决方案 - 直到锁存器/ barrierers可用。 谢谢大家的评论。
答案 2 :(得分:1)
只需最少的设计更改,您就可以使用信号量。每次产生推送到队列时,信号量开始为空并且上升。消费者在从队列中弹出之前首先尝试降低信号量。
C ++ 11不提供信号量实现,尽管可以使用互斥锁,条件变量和计数器进行模拟。 †
如果你真的想要生产者比消费者更快的无锁行为,你可以使用双重检查锁定。
/* producer */
bool was_empty = q.empty_lock_free();
q.push_lock_free(x);
if (was_empty) {
scoped_lock l(q.lock());
if (!q.empty()) {
q.cond().signal();
}
}
/* consumers */
for (;;) {
if (q.empty_lock_free()) {
scoped_lock l(q.lock());
while (q.empty()) {
q.cond().wait();
}
x = q.pop();
if (!q.empty()) {
q.cond().signal();
}
} else {
try {
x = q.pop_lock_free();
} catch (empty_exception) {
continue;
}
break;
}
}
答案 3 :(得分:0)
pthreads的一种可能性是,一个饥饿的线程与pause()
一起睡眠,并与SIGCONT
一起醒来。每个线程都有自己的awake
标志。如果生产者发布新输入时有任何线程处于睡眠状态,请使用pthread_kill()
唤醒一个。