http://yulingtianxia.com/blog/2015/11/01/More-than-you-want-to-know-about-synchronized/
文章目錄
1. 用到 @synchronized 的例子
2. 回到研究上來
本文翻譯自 Ryan Kaplan 的 More than you want to know about @synchronized
因為原文一些内容寫的不太準确,我按照我的了解做出了批注和補充。
如果你已經使用 Objective-C 編寫過任何并發程式,那麼想必是見過 @synchronized 這貨了。@synchronized 結構所做的事情跟鎖(lock)類似:它防止不同的線程同時執行同一段代碼。但在某些情況下,相比于使用 NSLock 建立鎖對象、加鎖和解鎖來說,@synchronized 用着更友善,可讀性更高。
譯者注:這與蘋果官方文檔對 @synchronized 的介紹有少許出入,但意思差不多。蘋果官方文檔更強調它“防止不同的線程同時擷取相同的鎖”,因為文檔在集中介紹多線程程式設計各種鎖的作用,是以更強調“相同的鎖”而不是“同一段代碼”。
如果你之前沒用過 @synchronized,接下來有個使用它的例子。這篇文章實質上是談談有關我對 @synchronized 實作原理的一個簡短研究。
用到 @synchronized 的例子
假設我們正在用 Objective-C 實作一個線程安全的隊列,我們一開始可能會這麼幹:
@implementation ThreadSafeQueue
{
NSMutableArray *_elements;
NSLock *_lock;
}
- (instancetype)init
{
self = [super init];
if (self) {
_elements = [NSMutableArray array];
_lock = [[NSLock alloc] init];
}
return self;
}
- (void)push:(id)element
{
[_lock lock];
[_elements addObject:element];
[_lock unlock];
}
@end
上面的 ThreadSafeQueue 類有個 init 方法,它初始化了一個 _elements 數組和一個 NSLock 執行個體。這個類還有個 push: 方法,它先擷取鎖、然後向數組中插入元素、最終釋放鎖。可能會有許多線程同時調用 push: 方法,但是 [_elements addObject:element] 這行代碼在任何時候将隻會在一個線程上運作。步驟如下:
線程 A 調用 push: 方法
線程 B 調用 push: 方法
線程 B 調用 [_lock lock] - 因為目前沒有其他線程持有鎖,線程 B 獲得了鎖
線程 A 調用 [_lock lock],但是鎖已經被線程 B 占了是以方法調用并沒有傳回-這會暫停線程 A 的執行
線程 B 向 _elements 添加元素後調用 [_lock unlock]。當這些發生時,線程 A 的 [_lock lock] 方法傳回,并繼續将自己的元素插入 _elements。
我們可以用 @synchronized 結構更簡要地實作這些:
@implementation ThreadSafeQueue
{
NSMutableArray *_elements;
}
- (instancetype)init
{
self = [super init];
if (self) {
_elements = [NSMutableArray array];
}
return self;
}
- (void)increment
{
@synchronized (self) {
[_elements addObject:element];
}
}
@end
在前面的例子中,”synchronized block” 與 [_lock lock] 和 [_lock unlock] 效果相同。你可以把它當成是鎖住 self,仿佛 self 就是個 NSLock。鎖在左括号 { 後面的任何代碼運作之前被擷取到,在右括号 } 後面的任何代碼運作之前被釋放掉。這爽就爽在媽媽再也不用擔心我忘記調用 unlock 了!
你可以給任何 Objective-C 對象上加個 @synchronized。那麼我們也可以在上面的例子中用 @synchronized(_elements) 來替代 @synchronized(self),效果是相同的。
回到研究上來
我對 @synchronized 的實作十分好奇并搜了一些它的細節。我找到了一些答案,但這些解釋都沒有達到我想要的深度。鎖是如何與你傳入 @synchronized 的對象關聯上的?@synchronized會保持(retain,增加引用計數)被鎖住的對象麼?假如你傳入 @synchronized 的對象在 @synchronized 的 block 裡面被釋放或者被指派為 nil 将會怎麼樣?這些全都是我想回答的問題。而我這次的收獲,會要你好看