在iOS10之後apple廢棄了
OSSpinLock
自旋鎖,使用os_unfair_lock
來替代。
在OSSpinLock
的api註釋中明確指出這是一個自旋鎖,那麼它的替代方案是一把什麼型別的鎖呢?
我們知道自旋鎖加鎖的時候,等待鎖的執行緒處於忙等狀態,並且佔用著CPU的資源。而互斥鎖加鎖的時候,等待鎖的執行緒處於休眠狀態,不會佔用CPU的資源。
那麼我們探就加鎖狀態下的等待鎖的執行緒的狀態就可以得出os_unfair_lock
這把鎖的型別。
探究os_unfair_lock的型別
依然使用上一篇中提到的賣票的案例。,分別使用OSSpinLock
和os_unfair_lock
加鎖,當第二條執行緒執行到加鎖程式碼的時候,是處於等待鎖的狀態,這個時候我們通過彙編程式碼窺探等待鎖的執行緒的狀態,得出其是自旋鎖還是互斥鎖。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, assign) int ticketCount;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化鎖
// ...
[self ticket];
}
- (void)saleTicket {
// 加鎖
// ...
int oldTicketCount = _ticketCount;
sleep(1); //讓執行緒睡眠1秒 更能體現多執行緒的隱患
oldTicketCount --;
_ticketCount = oldTicketCount;
NSLog(@"剩餘的票數%d",_ticketCount);
// 解鎖
// ...
}
- (void)ticket {
self.ticketCount = 20;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 5; i++) {
[self saleTicket];
}
});
}
複製程式碼
在Xcode中對加鎖程式碼進行斷點,然後點選工作列Debug->Debug Worlflow->Always Show Disassembly
。在xcode的lldb下使用si命令讓彙編程式碼一步一步的執行,觀察等待鎖的執行緒狀態:
我們發現執行緒進入了上述的狀態,這期是相當於一個while迴圈。這個迴圈等到自旋鎖解鎖之後進入下面的程式碼繼續執行。這就是自旋鎖忙等的狀態。
下面我們來看使用os_unfair_lock
的情況,使用它在程式碼註釋的地方進行初始化,並且進行加鎖和解鎖,重複上述操作進行觀察。
當彙編程式碼執行到這一行的時候不再繼續往下執行,斷點也失去了作用。這是因為syscall
呼叫了系統核心的函式,使得執行緒進入休眠狀態,不再佔用CPU資源。所以根據上面描述的自旋鎖和互斥鎖的區別os_unfair_lock
屬於互斥鎖。
自旋鎖和互斥鎖的比較
當預計執行緒等待鎖的時間很短,或者加鎖的程式碼(臨界區)經常被呼叫,但競爭情況很少發生,再或者CPU資源不緊張,擁有多核處理器的時候使用自旋鎖比較合適。
而當預計執行緒等待鎖的時間較長,CPU是單核處理器,或者臨界區有IO操作,或者臨界區程式碼複雜或者迴圈量大,臨界區競爭非常激烈的時候使用互斥鎖比較合適
總結
在iOS10之後apple已經不再建議使用OSSpinLock
自旋鎖了,它的替代方案是一個互斥鎖,所以一般情況下我們使用互斥鎖來解決執行緒同步的問題才是比較合理的。