天天看點

關于 @synchronized,這兒比你想知道的還要多

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 将會怎麼樣?這些全都是我想回答的問題。而我這次的收獲,會要你好看

繼續閱讀