同步模式
C++惯用法定界加锁(Scoped Locking)能确保当控制进入到某一个范围时,自动获得锁,而当控制离开该范围时,自动释放锁,不管从该范围返回的路径是什么。
策略化加锁(Strategized Locking)设计模式是策略模式的一种特例,它把同步机制参数化,这一机制保护临界区免受并发访问。
线程安全接口(Thread-Safe Interface)设计模式使加锁的开销减至最少,确保组件内部的方法调用不会因为要再次获得一个组件已经拥有的锁而“自死锁”。
如果程序执行期间临界区代码只能以线程安全的方式获得一次锁,双检查加锁优化(Double-Checked Locking Optimization)设计模式能够降低争用和同步开销。
4.1定界加锁(Scoped Locking)
1.问题
必须保护不能被并发执行的代码,方法是使用某些类型的锁,当控制进入或离开一个临界区时分别获得或者释放这种锁。但是,如果程序员必须显式地获得或释放锁,就难以保证在经过该代码的所有路径上锁都被释放了。
2.解决方案
定义一个哨兵(guard)类,当控制进入一个区域时,哨兵类的构造函数自动获得一个锁;当控制离开这个区域时,哨兵类的析构函数自动释放该锁。将哨兵类实例化,以在定义临界区的方法和块区域中获得或释放锁。
3.实现
1)定义一个哨兵类。其构造函数和析构函数分别获得和释放某种类型的锁。哨兵类的构造函数中存放一个指向锁的指针或对锁的引用,据此获得该锁。析构函数使用由构造函数存储的指针或引用释放该锁。
2)让临界区对应于哨兵对象的范围和生命期。要避免并发访问临界区,应指定一个区域,并由该区域的第一条语句在栈中创建一个哨兵对象。哨兵类的构造函数自动获得该锁。在离开临界区域时,自动调用释放该锁的析构函数。根据C++析构函数的语义,即使在临界区中抛出一个异常,这个保护锁也会被释放。
4.结论
优点:
1)增加了健壮性。通过使用这个方法,当控制进入或离开由C++方法和块区域定义的临界区时,会自动地获得和释放锁。这种方法消除了与同步和多线程有关的常见的编程错误,从而提高了并发应用程序的健壮性。
不足:
1)递归使用时可以发生死锁。如果一个使用定界加锁惯用法的方法递归地调用自己,当该锁不是一个“递归”的互斥时,就会发生“自死锁”。
2)受与语言相关的语义的限制。定界加锁惯用法是基于C++语言特性,因此不会和操作系统有关的系统调用集成起来。这样当进程或线程在一个受保护的临界区内部失败或退出时,锁就不能自动地释放。
3)过多的编译器警告。 定界加锁惯用法定义了一个在一个区域中没有被显式使用的哨兵对象,因为其析构函数隐含地释放锁。不幸的是,当在某区域定义了哨兵而没有显式地使用到哨兵时,一些C++编译器会显示"statement has no effect"的警告信息。