加锁机制-读《面向模式的软件体系架构:卷2》有感

最近读书总是零零碎碎, 所以养成个习惯, 读完后发个博客, 记录一下。

1、定界加锁

名字很高大上, 其实就是用类的构造函数加锁, 析构函数释放锁。
这是面向对象的优点, 防止某些语句或者异常跳出当前范围时, 锁没有释放。

2、策略化加锁

组件有时需要不同的的同步特性: 互斥锁, 读写锁, 信号灯(信号灯是什么鬼? 用到时再查吧), 空锁。
众所周知, 代码重用的方式主要为继承、 组合、 模板。
为了使用不同的锁, 这里讲了两种方式: 多态(其实就是组合)和参数化类型(其实就是模板)。
我很喜欢这里模板的写法, 所以简要写一下。

typedef fileCache<nullMutex> contentCache
typedef fileCache<threadMutex> contentCache
typedef fileCache<rwMutex> contextCache

当某处用到某种特性的该组件时,可以typedef一下, 非常的美观和清晰哈。

3、线程安全接口

一个类中的多个方法, 可能会相互调用, 造成自死锁。
方法1:
  改为递归锁, 但是本书说本方法实现效率偏低。(要仔细研究确认一下)
方法2:
 将所有的锁放在接口方法中,获取锁后, 通过实现方法(private或protected)执行实际操作, 返回后释放锁。
 优点: 实现方法思路清晰, 因其信任接口方法, 所以不必加锁。
 缺点: 加锁代码的范围较大, 并发效率不是很高。

4、双检查加锁优化

书中讲的也很笼统和片面, 按照我自己的认知来写吧。
以单例模式为例, 分为懒汉和饿汉。
饿汉:
static 变量在函数体外, 直接new(), 相当于全局变量。
线程安全。 缺点是如果该单例一直用不到, 就有点浪费了。
另外, 没有指定不同文件中全局变量的构造顺序(c++中全部变量是如何构造的,我还真不清楚, 不像c, 编译原理一目了然), 某些时候行不通。
懒汉:
单例用到时才初始化。

static xxx* instance() {
	if (instance_ == null) {
		std::lock_guard(mutex_);
		if (instance_ == null)
			instance = new xxx();
	}
	return instance_;
}

检查两次的原因是:当多个线程同时初始化时, 如果通过了第一检查, 会先后排队等待锁, 当最先拿到锁的线程实例化后, 其他线程就会跳过。不加第二个检查, 必然GG。
也可以想象将锁加在第一个检查之外, 效率就极低了。
问题:
  某些编译平台可能出问题。new后写内存的操作不是原子的, 时间如果巧合, 可能出现问题; 多处理器缓存的连贯性(什么鬼, 看不懂)。

猜你喜欢

转载自blog.csdn.net/weixin_42238721/article/details/91817757