在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對象更簡單。
將同步與異步派發結合起來,可以實現與普通加鎖機制一樣的同步行為,而這么做卻不會阻塞執行異步派發的形成。
使用同步隊列及柵欄塊,可以令同步行為更加高效。