在Objective-C中,如果有多个线程要执行同一份代码,那么有时可能会出问题。这种情况下,通常要使用锁来实现某种同步机制。
锁
- @synchronization
- (void)synchronizedMethod
{
@synchronized (self) {
//Safe
}
}
- @NSLock
_lock = [[NSLock alloc]init];
- (void)synchronizedMethod
{
[_lock lock];
//Safe
[_lock unlock];
}
这两种都会遇到死锁现象。而且其效率并不高。
GCD
- 串行同步队列(serial synchronization queue)。将读取操作及写入操作都安排在同一个队列里,即可保证数据同步。
_syncQueue = dispatch_queue_create("com.effectiveobjectivec.syncQueue", NULL);
- (NSString *)someString
{
__block NSString *localSomeString;
dispatch_sync(_syncQueue, ^{
localSomeString = self.someString;
});
return localSomeString;
}
- (void)setSomeString:(NSString *)someString
{
dispatch_sync(_syncQueue, ^{
self.someString = someString;
});
}
- 多个获取方法可以并发执行,而获取方法与设置方法之间不能并发执行,利用这个特点,可以优化代码。改为并发队列(concurrent queue)。
_syncQueue = dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
-(NSString *)someString {
__block NSString *localSomeString;
dispatch_sync (_syncQueue, ^{
localSomeString = _someString;
});
return localSomeString;
}
- (void)setSomeString:(NSString *)someString {
dispatch_barrier_async(_syncQueue, ^{
_someString = someString;
});
}
- 栏栅块(barrier block)必须单独执行,不能与其他块并行,这只对并发队列有意义,因为串行队列中的块总是按顺序逐个来执行的。并发队列如果发现接下来要处理的块是个栏栅块,那么就一直等到当前所有并发块都执行完毕,才会单独执行这个栏栅块。待栏栅块执行过后,再按正常方式继续向下处理。
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
设置方法中使用了栏栅块之后,对属性的读取操作依然可以并发执行,但是写入操作却必须单独执行了。
- (void)setSomeString:(NSString *)someString
{
dispatch_barrier_async(_syncQueue, ^{
self.someString = someString;
});
}
要点
派发队列可用来表述同步语义(synchronization semantic),这种做法要比使用@synchronized块或NSLock对象更简单。
将同步与异步派发结合起来,可以实现与普通加锁机制一样的同步行为,而这么做却不会阻塞执行异步派发的形成。
使用同步队列及栅栏块,可以令同步行为更加高效。