对std::队列的pop和push操作使用单独的互斥锁,而不是一个互斥锁

Using separate mutexs for pop and push operatin of std::queue instead of one

本文关键字:一个 单独 pop 队列 std push 操作      更新时间:2023-10-16

假设有一个生产者和多个消费者使用std::queue,为了保护std::queue中的数据,在访问std::queue时必须使用互斥锁

但是如果使用两个单独的锁呢?一个是pop,一个是push ?我认为使用两个单独的锁可能会更快。

我看了STL源代码。std::queue默认由std::deque实现。pop_front()push_back()使用两个独立的迭代器访问数据,一个用于第一个元素,一个用于最后一个元素。

void push_front(const value_type& __x){
    if (this->_M_impl._M_start._M_cur != this->_M_impl._M_start._M_first){
        this->_M_impl.construct(this->_M_impl._M_start._M_ur - 1, __x);
    } else {
        _M_push_front_aux(__x);
    }
}
void push_back(){
    if (this->_M_impl._M_finish._M_cur != this->_M_impl._M_finish._M_first){
        this->_M_impl.construct(this->_M_impl._M_finish._M_ur - 1, __x);
    } else {
        _M_pop_back_aux();
    }
}

因此,当一个线程正在pop队列中时,另一个线程会插队并尝试push_back。因为这两个操作使用不同的迭代器,指向deque的两个不同的结束点,所以这两个操作似乎都没问题?

pop_frontpush_back可能在内部访问不同的迭代器,但这就是抽象的全部意义:您不能确定这一点,而且不允许依赖它。

并且,正如已经在评论中指出的那样,还有size,这是由于c++ 11需要在O(1)中执行。为了满足这个要求,push_backpop_front都需要改变一些内部存储的大小,这是你肯定有竞争条件的地方。

然后想想当你的队列空了会发生什么…

总之:不要那样做。在并发设置中使用std::queue,你需要一个互斥锁来保护它。

作为另一种选择,您应该查看无锁数据结构。