synchronized的死锁
我们已经习惯了使用synchronized来做同步锁,但在实际开发中发现,写得不好容易出现死锁的坑。比如像下面这样的代码有两个个锁被公用,在多线程的情况下就容易导致死锁。
|
|
怎么写可以避免这种情况出现呢?改写的方法是在用到synchronized时传入一个内部NSObject成员变量,外部不能获取到这个变量,就不会形成交替死锁。
|
|
synchronized的性能
synchronized锁本身性能是会比其他锁要慢,有人对此做过测试。但是在实际使用中,锁本身的性能差异几乎可以忽略不计。
那为什么有人会抱怨用synchronized性能不好呢?
|
|
像上面这段被加锁的代码里,通过[self dosomethingelse]再调用别的方法。dosomethingelse方法的开发者,很可能并不知道自己的方法被锁同步的,他又可能调用别的函数,这样一层一层调用就可能变得更慢。
所以使用synchronized时一定要注意,尽量减少锁的范围和粒度。
不同数据使用不同锁,控制最小的粒度
减小加锁的范围,不必要加锁的代码放到外面
|
|
synchronized的失效
用synchronized时需要传入一个object,你有没有想过如果你传入的是nil会怎么样呢?
根据汇编代码可以发现,synchronized实现里会调用objc_sync_enter和objc_sync_exit
|
|
转换为
|
|
objc_sync_enter和objc_sync_exit函数定义在
其中objc_sync_enter函数的实现里,正常的情况obj不为nil时,会根据obj内存地址的哈希值查找合适的SyncData然后使用递归mutex加锁recursive_mutex_lock。到了objc_sync_exit时同样通过obj的内存地址的哈希值查找合适的SyncData,然后将其解锁recursive_mutex_unlock。
But,当执行objc_sync_enter函数时,如果传入的obj为nil,那并不会加锁,直接走到objc_sync_nil。也就是说,如果使用@synchronized时传入的obj为nil,那将不会加锁也就是失去了同步的效果。所以一定要保证obj不在执行期间被设置被nil。
|
|
|
|
在工程里,将obj设置为nil,给objc_sync_nil加个断点,会看到确实走到objc_sync_nil函数
参考
synchronized的实现
objc-sync源码
关于 @synchronized,这儿比你想知道的还要多
正确使用多线程同步锁@synchronized()