const
真理大讨论之 const
的线程安全
有时我们会听到const
就一定线程安全这样的说法,但是事实可能没那么简单。
线程安全的const
有时候人们好像认为只要加一个const
就能自动地给一个变量附上一个“线程安全”的属性,因而在多线程环境中就可以随意地访问。
一般而言,不可变 的数据可以在不考虑线程安全的情况下被访问。这是因为数据如果不可变,那么我们就不会去写它,无论什么时候、怎样去读、从哪个线程读,我们都能得到同样的结果,所以它就是线程安全的。
但是需要澄清的是,尽管声明一个变量是const
类型是一个创建不可变数据的好方法,但是这并不是说,有const
修饰的数据就一定是不可变的。
在之前关于const
正确性的文章中,我们就列举去了几种特例。在特例中,我们可以通过绕过const
变量的方式,访问实际语义const
的数据,并改变它。要么是在方法中有mutable
变量,要么是其中牵扯到了指针传递。不管怎么说,这种方式都不会被编译器所禁止,或者识别,造成了语法const
并没有达到语义const
效果的情况。
那么相反也是成立的,也就是说,有些数据是可变的,即使我们用一个没有主动声明为const
的接口去访问他,它也是绝对的线程安全。当然,这是由于我们没有开发出适当的接口,我们可以通过给接口增加const
属性来解决这个问题。
是不是该全面禁止使用mutable
,并让所有间接数据访问都变成const
?
简而言之:不是
在每次我们使用mutable
和间接访问的时候我们当然都要好好地考虑一下,因为它们确实对我们的线程安全产生了威胁。但是那也就不是说mutable
和间接访问就应该在多线程代码中完全被禁止。
并不是所有的数据都有必要在线程中被共享。因此,使用不线程安全的类也是可以的,只要这个类从始至终只被一个线程用到。另一方面,虽然让我们的数据变得“不可变”是保证线程安全的有效手段,但他们却不是唯一手段。
Mutex以及所有其他的用来保证线程安全的手段就是其他方式。当然,你确实可以不用lock然后码出一份漂亮的线程安全的代码。但是,假如你的技艺还没有如此精湛,或者没那么多闲功夫,还是不要尝试写lock-free的线程安全类吧。一方面很困难不说,另一方面如何去证明以及交叉验证你的方案的正确性也是很让人恼火的事。
结语
为了达到同样的目的,C++确实在语言特性的选择上给了我们很多自由。我们真正该做的是,不要总是秉持着“const
就一定线程安全”这样的思想,而忘记想一想可能发生问题的地方。