简体   繁体   English

在无锁实现中没有互斥的condition_variable

[英]condition_variable without mutex in a lock-free implementation

I have a lock-free single producer multiple consumer queue implemented using std::atomics in a way similar to Herb Sutters CPPCon2014 talk. 我有一个使用std::atomics实现的无锁单生成器多个使用者队列,其方式类似于Herb Sutters CPPCon2014 talk。

Sometimes, the producer is too slow to feed all consumers, therefore consumers can starve. 有时,生产者太慢,无法养活所有消费者,因此消费者可能会饿死。 I want to prevent starved consumers to bang on the queue, therefore I added a sleep for 10ms . 我想阻止饥饿的消费者在排队等待,因此我加入了10ms的睡眠。 This value is arbitrary and not optimal. 该值是任意的,不是最佳的。 I would like to use a signal that the consumer can send to the producer once there is a free slot in the queue again. 我想使用一个消息,一旦队列中有一个空闲插槽,消费者就可以发送给生产者。 In a lock based implementation, I would naturally use std::condition_variable for this task. 在基于锁的实现中,我自然会将std::condition_variable用于此任务。 However now in my lock-free implementation I am not sure, if it is the right design choice to introduce a mutex , only to be able to use std::condition_variable . 但是现在在我的无锁实现中我不确定,如果它是引入mutex的正确设计选择,只能使用std::condition_variable

I just want to ask you, if a mutex is the right way to go in this case? 我只是想问你,在这种情况下, mutex是否是正确的方法?

Edit: I have a single producer, which is never sleeping. 编辑:我有一个生产者,从不睡觉。 And there are multiple consumer, who go to sleep if they starve. 并且有多个消费者,如果他们饿死就会入睡。 Thus the whole system is always making progress, therefore I think it is lock-free. 因此整个系统总是在取得进展,因此我认为它是无锁的。 My current solution is to do this in the consumers GetData Function: 我目前的解决方案是在消费者GetData函数中执行此操作:
std::unique_lock<std::mutex> lk(_idleMutex); _readSetAvailableCV.wait(lk);

And this in the producer Thread once new data is ready: 一旦新数据准备好,这就在生产者线程中:
_readSetAvailableCV.notify_all();

If most of your threads are just waiting for the producer to enqueue a resource, I'm not that sure a lock-free implementation is even worth the effort. 如果你的大多数线程只是等待生产者排队资源,我不确定无锁实现是否值得付出努力。 most of the time, your threads will sleep, they won't fight each other for the queue lock. 大多数时候,你的线程会睡觉,他们不会为了队列锁而互相争斗。

That is why I think (from the amount of data you have supplied), changing everything to work with a mutex + conditional_variable is just fine. 这就是为什么我认为(从您提供的数据量),改变所有使用互斥+ + conditional_variable的工作就好了。 When the producer enqueues a resource it notifies just one thread (with notify_one() ) and releases the queue lock. 当生产者将资源排入队列时,它只通知一个线程(使用notify_one() )并释放队列锁。 The consumer that locks the queue dequeues a resource and returns to sleep if the queue is empty again. 锁定队列的使用者将资源出列,并在队列再次为空时返回休眠状态。 There shouldn't be any real "friction" between the threads (if your producer is slow) so I'd go with that. 线程之间不应该有任何真正的“摩擦”(如果你的制作人很慢),所以我会选择它。

I just watched this CPPCON video about the concurrency TS: Artur Laksberg @cppcon2015 我刚观看了CPPCON关于并发TS的视频: Artur Laksberg @ cppcon2015

Somewhere in the middle of this talk Artur explains how exactly my problem could be solved with barriers and latches. 在这次演讲的中间,Artur解释了我的问题究竟是如何通过障碍和锁存来解决的。 He also shows an existing workaround using a condition_variable in the way i did. 他还以我的方式使用condition_variable显示现有的解决方法。 He underlines some weakpoints about the condition_variable used for this purpose, like spurious wake ups and missing notify signals before you enter wait. 他强调了一些关于用于此目的的condition_variable的弱点,比如在你进入等待之前的虚假唤醒和丢失通知信号。 However in my application, these limitations are no problem, so that I think for now, I will use the solution that I mentioned in the edit of my post - until latches/barrierers are available. 但是在我的应用程序中,这些限制没有问题,所以我想现在,我将使用我在编辑帖子中提到的解决方案 - 直到锁存器/ barrierers可用。 Thanks everybody for commenting. 谢谢大家的评论。

With minimal design change to what you have, you can simply use a semaphore. 只需最少的设计更改,您就可以简单地使用信号量。 The semaphore begins empty and is upped every time the produces pushes to the queue. 每次产生推送到队列时,信号量开始为空并且上升。 Consumers first try to down the semaphore before popping from the queue. 消费者首先尝试在从队列中弹出之前降低信号量。

C++11 does not provide a semaphore implementation, although one can be emulated with a mutex, a condition variable, and a counter. C ++ 11不提供信号量实现,尽管可以使用互斥锁,条件变量和计数器进行模拟。

If you really want lock-free behavior when the producer is faster than the consumers, you could use double checked locking. 如果你真的想要生产者比消费者更快的无锁行为,你可以使用双重检查锁定。

/* 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;
    }
}

One possibility with pthreads is that a starved thread sleeps with pause() and wakes up with SIGCONT . pthreads的一种可能性是一个饥饿的线程用pause()休眠并用SIGCONT唤醒。 Each thread has its own awake flag. 每个线程都有自己的awake标志。 If any thread is asleep when the producer posts new input, wake one up with pthread_kill() . 如果生产者发布新输入时有任何线程处于睡眠状态,请使用pthread_kill()唤醒一个线程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM