const 和 mutable在C++存在已经很多年了,对于如今的这两个关键字你了解多少?
Problem
JG Question
1. 什么是“共享变量”?
Guru Question
2. const 和 mutable对于共享变量来说意味着什么?
3. 在C++98和C++11两者间,const 和 mutable有什么区别?
Stop and thinking……
Solution
1. 什么是“共享变量”?
共享变量是在同一时间可以被多个线程同时访问的变量。
这个概念在C++内存模型中很重要。例如:C++内存模型禁止创造一个写入“潜在共享变量区”,这不会被写入到程序执行的顺序一致(sequentially consistent)。当C++标准库禁止通过(其他)线程访问修改对象指的就是这节。
2. const 和 mutable对于共享变量来说意味着什么?
从C++11开始,变量加上const可能共享对于并发来说,意思是“只读或几乎和只读一样”。在同一对象上的并发const操作要求在没有调用代码做外部同步是安全的。如果你在实现一个类型,除非你知道这个类型的对象绝不会被共享(通常不可能),这几意味着你的每一个const成员函数必须满足其下一点:
· 这个对象真正的是物理上/按位的const,意味着在对象的数据上不执行写动作;
· 内部同步,这样如果在对象的数据上做了写的动作,那么数据可以通过mutex或者同等性质的(或者是atomic<>)来保证数据的正确性,因此任何可能多调用的并发const访问都不能辨别其中的差别。(相同的)
类型没有遵循以上任意其中一点,那么都不能和标准库一起使用。详细参照ISO C++ §17.6.5.9
同样地,在mutable的成员变量上写总意味着:此变量是“可写的但逻辑上是const的”。这意味着:
·“逻辑上const”部分的意思是“可以被多个并发const操作安全地使用”;
·”mutable“和”可写性“部分的意思是一些const操作可能确实会对共享变量进行写的动作,这意味着在对变量进行读和写是内在正确的,因此它应该使用mutex或相同的东西来保护,或者使用atomic<>。
通常情况下:
Guideline:记住”M&M规则“:对于成员变量来说,mutable和mutex(或atomic)是一起使用的。
这是双向的,也就是:
·(1)对于成员变量,mutable意味着mutex(或等价物):一个mutable的成员变量假定是一个可变的共享变量,因此必须是内在同步的,使用mutex,称atomic或者同类的东西来保护。
·(2)对于成员变量,mutex(或同等的同步类型)意味着mutable:一个成员变量它本身是同步类型,比如mutex或条件变量(condition variable),自然地需要mutable,因为你可能会在并发的const成员函数中以非const的方式来使用它(比如:使用std::lock_guard<mutex>)
我们或在GotW #6b的第二部分看到例子。
3. 在C++98和C++11两者间,const 和 mutable有什么区别?
首先澄清一点:C++98单线程代码依旧是能正常运行的。C++11对C++98有相当好的兼容,即使是在const的语义上有着进化。C++98单线程代码使用老的”逻辑const“意味着const依然是有效的。
随着C++98,我们告诫一整代C++开发者,”const意味的是逻辑上的const,不是物理上/按位的const“。这也就是,在C++98中,我们教授的是const只意味着对象的可观察状态(就是说,通过非private成员函数)不应该改变直到调用者调用。但是它的内部位可能是改变了的,为了更新计数器和一些设施,其他数据通过类型的public或protected接口是不能访问的。
这些定义对于并发来说是不够的。在C++11和之后的版本中,标准库中包含了并发的内存模型和线程安全规范。这也使得更简单一些:现在const真的是意味着”只读“或安全的并发读取,要不真正在物理上/按位const,或者内部同步。因此对于任意可能的并发const访问的写都是同步的,这样调用者就不会知道其中存在不同。
尽管已存在的C++98年代的类型在C++98年代的单线程中依旧能运行正常,这些类型和一些你现在所编写的类型都应该遵循新的更严格的要求,如果它们可能用在多线程中。好消息是那么已经存在的类型已经遵循了这个规则。在单线程中依赖于强制去掉const和/或使用mutable的数据成员已经被普遍质疑和罕见。
原文地址:http://herbsutter.com/2013/05/24/gotw-6a-const-correctness-part-1-3/