Mutex的锁定/解锁功能应该是“const”吗?

时间:2010-08-26 08:58:55

标签: c++ multithreading api

我正在维护一个包含Mutex类的库。我无法确定此类公开的lock()和unlock()函数是否应该是const。我在Web上寻找类似的代码,并且可以找到两种实现方式。

第一个实现,lock()和unlock()不是const。这意味着在const函数中使用Mutex类的人应该付出额外的努力来调用Mutex的函数:

class Mutex
{
public:
   void lock();
   void unlock();
};

class Foo
{
public:
   void getBar(Bar& bar) const
   {
      m_mutex.lock();
      bar = m_bar;
      m_mutex.unlock();
   }

private:
   // Mutable, responsability of the user (or we could use a const_cast when needed)
   mutable Mutex m_mutex;
   Bar   m_bar;
};

第二个实现,lock()和unlock()是const,即使这听起来不太自然(因为我们修改了Mutex实例),但用户在他的一个const中调用这些函数时不必费心功能:

class Mutex
{
public:
   void lock() const;
   void unlock() const;
};

class Foo
{
public:
   void getBar(Bar& bar) const
   {
      m_mutex.lock();
      bar = m_bar;
      m_mutex.unlock();
   }

private:
   Mutex m_mutex;
   Bar   m_bar;
};

您更喜欢什么样的解决方案?我希望你的意见能帮助我做出决定。

4 个答案:

答案 0 :(得分:10)

mutable是为这种东西而制作的。即,mutable适用于不参与对象的逻辑const 的事物。 (如果一个值是“逻辑常数”,则表示“主要”值是常量;对象的值从外部不能改变。)

您的对象的值是互斥锁状态的独立(它只是一个提供一致性的实现细节,并且它的状态在类外是未知的),这是一个好兆头互斥量应为mutable


请注意,您不应该使用const_cast路线。 This leads to undefined behavior如果您这样做:

const Foo f;

Bar b;
f.getBar(b); // takes const off of mutex, modifies (bang you're dead)

答案 1 :(得分:8)

我更喜欢在客户端代码中生成Mutex mutable的第一个选项。这样,该类的用户明确地知道他正在从他的const函数调用非const函数。实际上,如果您看到CMutex类的MFC实现,则可以看到LockUnlock方法是非常量的。

答案 2 :(得分:3)

第一个实现是首选的:const条件是指逻辑const 二进制常量:互斥是一个帮助类 - 它实际上并不是更改客户端代码的逻辑状态,但更改它确实会更改客户端代码的二进制状态。

因此,应在客户端代码中将互斥锁设为mutable

如果你在代码中缓存一些代价高昂的操作结果,就会出现同样的情况:你将chache变量声明为可变的,所以在计算它时你不需要抛弃类的const - 这个

答案 3 :(得分:0)

如果你想从Mutex类派生类,那么使(两些)函数成为常量是有意义的,那么你可以在派生类自己的const函数中调用它。

像这样:

class Foo : public Mutex {
public:
    void
        bar() const { lock(); doSomething(); unlock(); }
}