如何在初始化列表中锁定互斥锁?

时间:2013-04-24 15:58:39

标签: c++ multithreading c++11

我有一个ConcurrentQueue类,它基于用户提供的容器,并带有这样的构造函数...

ConcurrentQueue(const ConcurrentQueue& other) : m_Queue(other.m_Queue) {}

但是,我需要在复制时锁定other的互斥锁。

选项1:

所以我根本无法使用复制构造函数,并且......

ConcurrentQueue(const ConcurrentQueue& other) : m_Queue(other.m_Queue)
{
    std::lock_guard<std::mutex> lock(other.m_Mutex);
    m_Queue = other.m_Queue;
}

但我不能保证复制分配和复制构造是等效的功能。

选项2:

我可以有私人方法......

std::queue<T, Container> GetQueue() const
{
    std::lock_guard<std::mutex> lock(other.m_Mutex);
    return m_Queue;
}

然后在构造函数中执行此操作...

ConcurrentQueue(const ConcurrentQueue& other) : m_Queue(other.GetQueue()) {}

但是这可能(取决于优化)使用m_Queue的复制构造函数一次,它移动构造函数一次。我也不能保证副本和移动只相当于副本。此外,用户提供的容器可能是奇怪的并且可以复制但不可移动,这也会导致这种方法出现问题。

那么,我该怎么办?

2 个答案:

答案 0 :(得分:9)

ConcurrrentQueue::ConcurrrentQueue(
        ConcurrrentQueue const& other )
    : m_Queue( (std::lock_guard<std::mutex>( other.m_Mutex ),
               other.m_Queue ) )
{
}

应该有用。

答案 1 :(得分:1)

锁定,创建内容的副本,然后将其与成员交换。至少那是最简单和恕我直言最干净的方式。另一种不太干净的方法是使用逗号运算符:(a, b)产生b,但如果a是一个范围锁定,临时将生效到下一个序列点,即直到你使用b初始化您的本地副本。

那就是说,有两件事需要考虑:

  • 无论如何,复制也不是一个聪明的想法,如果你只是禁用复制,你的设计也会起作用。
  • 如果您有权访问该队列并且可以将其读取以进行复制,那是否意味着该互斥锁必须已被锁定?如果没有,您如何确定要真正复制队列?我不怀疑有答案证明设计是合理的,但这是不寻常的。