C++ 互斥体应该是可变的吗?

C++ 互斥体应该是可变的吗?,c++,mutex,mutable,C++,Mutex,Mutable,不确定这是一个风格问题,还是一个有严格规则的问题 如果我想让公共方法接口尽可能保持常量,但使对象线程安全,我应该使用可变互斥体吗?一般来说,这是一种好的风格,还是应该首选非常量方法接口?请证实您的观点。[答案已编辑] 基本上,对可变互斥体使用const方法是一个好主意(顺便说一句,不要返回引用,确保按值返回),至少表明它们不修改对象。互斥体不应该是常量,将锁定/解锁方法定义为常量将是无耻的谎言 实际上,这(和记忆)是我看到的唯一合理使用mutable关键字的方法 您还可以使用对象外部的互斥体:将

不确定这是一个风格问题,还是一个有严格规则的问题

如果我想让公共方法接口尽可能保持常量,但使对象线程安全,我应该使用可变互斥体吗?一般来说,这是一种好的风格,还是应该首选非常量方法接口?请证实您的观点。

[答案已编辑]

基本上,对可变互斥体使用const方法是一个好主意(顺便说一句,不要返回引用,确保按值返回),至少表明它们不修改对象。互斥体不应该是常量,将锁定/解锁方法定义为常量将是无耻的谎言

实际上,这(和记忆)是我看到的唯一合理使用
mutable
关键字的方法

您还可以使用对象外部的互斥体:将所有方法安排为可重入的,并让用户自己管理锁:
{lock locker(the_mutex);obj.foo();}
不是很难键入,而且

{
    lock locker(the_mutex);
    obj.foo();
    obj.bar(42);
    ...
}

它的优点是不需要两个互斥锁(并且可以保证对象的状态不会更改)。

隐藏的问题是:您将保护类的互斥锁放在哪里

作为总结,假设您希望读取受互斥锁保护的对象的内容

“read”方法在语义上应该是“const”,因为它不会更改对象本身。但要读取该值,需要锁定一个互斥体,提取该值,然后解锁互斥体,这意味着必须修改互斥体本身,这意味着互斥体本身不能是“const”

如果互斥是外部的 那么一切都好了。对象可以是“const”,互斥体不需要是:

Mutex mutex ;

int foo(const Object & object)
{
   Lock<Mutex> lock(mutex) ;
   return object.read() ;
}
内部互斥体解决方案是一个很好的解决方案:一方面必须在另一个对象附近声明一个对象,另一方面将它们都聚合到包装器中,最终也是一样的

但聚合有以下优点:

  • 更自然(在访问对象之前锁定对象)
  • 一个对象,一个互斥体。由于代码样式强制您遵循此模式,因此它降低了死锁风险,因为一个互斥体将仅保护一个对象(而不是您不会真正记住的多个对象),而一个对象将仅由一个互斥体保护(而不是由需要按正确顺序锁定的多个互斥体保护)
  • 上面的静音类可以用于任何类
  • 因此,让您的互斥对象尽可能靠近互斥对象(例如,使用上面的互斥构造),并为互斥对象选择
    mutable
    限定符

    编辑2013-01-04 显然,Herb Sutter有着相同的观点:他对C++11中
    const
    mutable
    的“新”含义的介绍非常有启发性:


    我是一个POSIX noob。然而,我读到信号量被实现为futex,它维护等待锁的进程的FIFO。按值传递是个好主意吗?@San J.你不是按值传递互斥体,而是按值传递你要读取的属性。Alexandre,第一行不需要是
    锁锁锁锁(互斥体)
    ?(还有一些方法不必提供
    mutex
    template参数。)
    mutable
    还有其他合理的用法。缓存非平凡GET的结果是一大类可变公平的地方。@VoidStar:这就是我所说的“记忆化”的意思。请参阅我对getter的看法。@San:确实,您经常需要计算或读取类中的某些结果。这不是一个getter:a
    struct进程{bool start()const;void start();}既没有setter,也没有getter。只有方法。相反,a
    struct employee{int get_salary()const;void set_salary(int);}让我呕吐。@San:我不太理解这个例子,但这并不重要。一、 有时候,也会写getter。(我甚至写过二传。)但通常这是设计错误的暗示。@Marcin我没有挑剔。我在学习。很抱歉,我在一个免费的网站上浪费了你的时间,这个网站有免费的空间来提问和获得免费的建议。我可以向谁结账老实说,我应该把它换成一个新问题。很抱歉,我打断了你的回答。“我甚至都没想过。”马辛:谢天谢地。这肯定让你的问题在SO的头版上出现了好几个小时。我可以看出你仍然没有更多的答案,但我认为这是因为你陈述问题的方式不清楚。正如我在前面的评论中所说,我认为没有必要在互斥体中使用getter/setter,所以我不知道如何回答您的问题。发布您的互斥锁的可能设计,以便我们知道我们在谈论什么。这当然是一个很好的解释,
    +1
    。不过,我不确定这是否解决了OP的问题,因为他的问题太模糊了。@sbi:谢谢!事实上,我也遇到了同样的问题,所以我觉得我必须添加一些上下文。OP的隐藏信息是,他正在使用一个隐藏在他想要保护的类中的互斥锁,但没有说清楚(我误读了第一个答案,并认为Alexandre C.只建议使用外部互斥锁,当他用五行文字写下我花了一本书左右的时间解释的时候,所以我也应该受到责备):-。。。很好的解释。这正是我想要的。谢谢你的Herb Sutter视频!值得花时间。
    template <typename T>
    class Mutexed : public T
    {
       public :
          Mutexed() : T() {}
          // etc.
    
          void lock()   { this->m_mutex.lock() ; }
          void unlock() { this->m_mutex.unlock() ; } ;
    
       private :
          Mutex m_mutex ;
    }
    
    int foo(const Mutexed<Object> & object)
    {
       Lock<Mutexed<Object> > lock(object) ;
       return object.read() ;
    }
    
    template <typename T>
    class Mutexed : public T
    {
       public :
          Mutexed() : T() {}
          // etc.
    
          void lock()   const { this->m_mutex.lock() ; }
          void unlock() const { this->m_mutex.unlock() ; } ;
    
       private :
          mutable Mutex m_mutex ;
    }