对 std::queue 的弹出和推送操作使用单独的互斥体而不是一个

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

假设有一个生产者和多个消费者,他们使用一个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,您需要一个互斥体来保护它。

作为替代方案,您应该研究无锁数据结构。