多用派发队列, 少用同步锁
在 OC 中,如果有多个线程要执行同一份代码, 那么有时可能会出现问题, 这种情况下, 通常要使用锁来实现某种同步机制, 在 GCD 出现之前, 有两种办法, 第一种是采用内置的 '同步快'.
- (void)synchronizedMethod{
@synchronized(self){
// self
}
}
这种写法会根据给定的对象, 自动创建一个锁,并等待块中的代码执行完毕,执行到这段代码的结尾处, 锁就释放了, 在本例中,同步行文所执行的对象就是 self, 这么写通常没有错, 因为它可以保证每个对象实例都能不受干扰的运行其 synchronizedMethod 方法, 然而,滥用 @synchronized (self) 会降低代码效率, 因为共用同一个锁的那些同步块. 都必须按顺序执行,若是在 self 对象上频繁的加锁, 那么程序可能要等另一段与此无关的代码执行完毕, 才能继续执行当前代码, 这样做其实没有必要.
另一个办法是直接使用 NSLock 对象:
_lock = [[NSLock alloc] init];
- (void)synchronizedMethod{
[_lock lock];
//self
[_lock unlock];
}
也可以使用 NSRecursiveLock 这种 '递归锁',线程能够多次持有该锁,而不会出现死锁现象,
这两种方法都很好,但是都有缺陷
替换方案是使用 GCD, 它能更简单,更高效的形式为代码加锁,那就是使用串行同步队列,
将读取操作及写入操作都安排在同一个队列里面, 即可保证数据同步.其用法如下:
_syncQueue = dispatch_queue_create("com.effectiveobjectivec,syncQueue",NULL);
- (NSString *)someString{
__block NSString *localSomeString;
dispatch_sync(_syncQueue,^{
localSomeString = _someString;
});
return localSomeString;
}
- (void)setSomeString:(NSString *)someString{
dispatch_sync(_syncQueue,^{
_someString = someString;
});
}
总结: 派发队列可用来表述同步语义, 这种做法要比使用 @synchronized 块 或者 NSLock 对象变得更简单.
将同步与异步派发结合起来, 可以实现与普通加锁机制一样的同步行为, 而这么做却不会阻塞执行异步派发的线程.
使用同步队列及栅栏块, 可以令同步行为更加高效,