如何在初始值设定项列表中锁定互斥对象

How can I lock a mutex in an initializer list?

本文关键字:列表 锁定 对象      更新时间:2023-10-16

我有一个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的复制构造函数一次,移动构造函数一次。我也不能保证一份副本和一次移动就等于一份副本。此外,用户提供的容器可能很奇怪,可以复制但不可移动,这也会导致这种方法出现问题。

那么,我该怎么办?

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

应该起作用。

锁定,创建内容的副本,然后与成员交换。至少这是最简单和IMHO最干净的方法。另一种不太干净的方法是使用逗号运算符:(a, b)产生b,但如果a是作用域锁,则临时锁将一直存在到下一个序列点,即,直到您使用b初始化本地副本。

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

  • 也许无论如何,复制并不是一个明智的想法,如果你只是禁用复制,你的设计也会很好
  • 如果您有权访问队列,并且可以读取它进行复制,这难道不意味着互斥锁一定已经被锁定了吗?如果不是,你如何确定你真的想复制队列?我不质疑是否有答案可以证明这个设计是合理的,但这很不寻常