第二章 线程安全性
持有锁时间过长会带来活跃性和性能问题。
第三章 对象的共享
可见性
不同线程的可见性导致在竞态条件下可能是失效数据,所以需要同步措施来保证数据正确性
volatile变量 当且仅当满足以下所有条件时,才应该使用volatile变量:
1.对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值
2.该变量不会与其他状态变量一起纳入不变性条件中
3.在访问变量时不需要加锁
线程封闭
__thread是GCC内置的线程局部存储设施,它的实现非常高效。
__thread使用规则:只能用于修饰POD类型,不能修饰class类型,因为无法自动调用构造函数和析构函数。
__thread可以用于修饰全局变量、函数内的静态变量,但不能用于修饰栈上变量和class的成员变量。
__thread变量的初始化只能用编译器常量。
__thread变量是每个线程有一份独立实体
如果__thread变量为class类型指针,如要定制他的释放函数,可以参见muduo中的threadlocal类
c++11 thread_local代表了一个全局的变量,而在每个线程中都各自new一个线程本地的对象交给它进行管理,这样,各个线程就可以各自独立地访问这个全局变量的本地存储版本,线程之间就不会因为访问同一全局对象而引起资源竞争导致性能下降。而线程结束时,这个资源会被自动释放。
但是滥用thread_local变量会降低代码的可重用性。
不变性
不可变对象一定是线程安全的,不可变对象需要满足在对象创建后其状态是不能修改的,需要保证对象是被安全完整的创建的。可能类似于lazy_property, 由于const属性所以作为共享对象时就是安全的。
说到不可变对象,可以谈谈mutable,如果类的成员函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是,有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员就应该被mutable来修饰。比如一个安全队列中的mutex可以声明为mutable。
对象安全发布的常用模式:
1.在静态初始化函数中初始化一个对象引用(注:或者类似静态局部变量)
2.将对象的引用保存到volatile域,当然前边已经提到了这种情况主要用于做标识用
3.将对象的引用保存到一个由锁保护的域中
在并发程序中使用和共享对象时,可以使用一些常用的策略:
线程封闭;只读共享;线程安全对象;锁保护对象
第四章 对象的组合
在设计线程安全的类时需要考虑的三个基本要素:
1.找出构成对象状态的所有变量
2.找出约束状态变量的不变性条件
3.建立对象状态的并发访问管理策略