目錄
- 1、為什麼要執行緒安全
- 2、自旋鎖和互斥鎖
- 3、鎖的型別
- 1、OSSpinLock
- 2、os_unfair_lock
- 3、pthread_mutex
- 4、dispatch_semaphore
- 5、dispatch_queue(DISPATCH_QUEUE_SERIAL)
- 6、NSLock
- 7、NSRecursiveLock
- 8、NSCondition
- 9、NSConditionLock
- 10、@synchronized
- 11、pthread_rwlock
- 12、dispatch_barrier_async
- 13、atomic
- 4、鎖的效能比較
為什麼要執行緒安全
多個執行緒訪問同一塊資源的時候,很容易引發資料混亂問題。
一個大家都喜歡拿來舉例子的就是買票demo
,今天我使用這個案例
假設有100張票,同時開5個視窗買票,5個視窗買票,我們來看看結果
//賣票演示
- (void)ticketTest{
self.ticketsCount = 50;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (NSInteger i = 0; i < 5; i++) {
dispatch_async(queue, ^{
for (int i = 0; i < 10; i++) {
[self sellingTickets];
}
});
}
}
//賣票
- (void)sellingTickets{
int oldMoney = self.ticketsCount;
sleep(.2);
oldMoney -= 1;
self.ticketsCount = oldMoney;
NSLog(@"當前剩餘票數-> %d", oldMoney);
}
複製程式碼
正常情況下我有50張票,然後賣了50次,剩餘票數應該是0,但是列印結果竟然是3,所以這裡就存在了執行緒安全問題。
出現執行緒安全的原因
出現執行緒安全的原因就是在同一個時間,多個執行緒同時讀取一個值,像執行緒A和B同時讀取了當前票數為10,等於是賣了兩張票,但是總票數其實就減少了一張。
解決方法
使用執行緒同步技術,按照預定的先後次序依次進行,常見的執行緒同步技術就是加鎖
自旋鎖和互斥鎖
自旋鎖(Spin lock)
自旋鎖與互斥鎖有點類似,只是自旋鎖不會引起呼叫者睡眠,如果自旋鎖已經被別的執行單元保持,呼叫者就一直迴圈在那裡看是 否該自旋鎖的保持者已經釋放了鎖,"自旋"一詞就是因此而得名。其作用是為了解決某項資源的互斥使用。因為自旋鎖不會引起呼叫者睡眠,所以自旋鎖的效率遠 高於互斥鎖。雖然它的效率比互斥鎖高,但是它也有些不足之處: 1、自旋鎖一直佔用CPU,他在未獲得鎖的情況下,一直執行--自旋,所以佔用著CPU,如果不能在很短的時 間內獲得鎖,這無疑會使CPU效率降低。 2、在用自旋鎖時有可能造成死鎖,當遞迴呼叫時有可能造成死鎖,呼叫有些其他函式也可能造成死鎖,如 copy_to_user()、copy_from_user()、kmalloc()等。 因此我們要慎重使用自旋鎖,自旋鎖只有在核心可搶佔式或SMP的情況下才真正需要,在單CPU且不可搶佔式的核心下,自旋鎖的操作為空操作。自旋鎖適用於鎖使用者保持鎖時間比較短的情況下。
互斥鎖
互斥鎖屬於sleep-waiting型別的鎖。例如在一個雙核的機器上有兩個執行緒(執行緒A和執行緒B),它們分別執行在Core0和 Core1上。假設執行緒A想要通過pthread_mutex_lock操作去得到一個臨界區的鎖,而此時這個鎖正被執行緒B所持有,那麼執行緒A就會被阻塞 (blocking),Core0 會在此時進行上下文切換(Context Switch)將執行緒A置於等待佇列中,此時Core0就可以執行其他的任務(例如另一個執行緒C)而不必進行忙等待。而自旋鎖則不然,它屬於busy-waiting型別的鎖,如果執行緒A是使用pthread_spin_lock操作去請求鎖,那麼執行緒A就會一直在 Core0上進行忙等待並不停的進行鎖請求,直到得到這個鎖為止。
兩種鎖的加鎖原理
互斥鎖:執行緒會從sleep(加鎖)——>running(解鎖),過程中有上下文的切換,cpu的搶佔,訊號的傳送等開銷。
自旋鎖:執行緒一直是running(加鎖——>解鎖),死迴圈檢測鎖的標誌位,機制不復雜。
對比 互斥鎖的起始原始開銷要高於自旋鎖,但是基本是一勞永逸,臨界區持鎖時間的大小並不會對互斥鎖的開銷造成影響,而自旋鎖是死迴圈檢測,加鎖全程消耗cpu,起始開銷雖然低於互斥鎖,但是隨著持鎖時間,加鎖的開銷是線性增長。
兩種鎖的應用
互斥鎖用於臨界區持鎖時間比較長的操作,比如下面這些情況都可以考慮
- 1 臨界區有IO操作
- 2 臨界區程式碼複雜或者迴圈量大
- 3 臨界區競爭非常激烈
- 4 單核處理器
至於自旋鎖就主要用在臨界區持鎖時間非常短且CPU資源不緊張的情況下,自旋鎖一般用於多核的伺服器。
13種鎖
1、OSSpinLock
OSSpinLock叫做”自旋鎖”,使用時需要匯入標頭檔案#import <libkern/OSAtomic.h>
//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
//加鎖
OSSpinLockLock(&lock);
//解鎖
OSSpinLockUnlock(&lock);
複製程式碼
demo
#import "OSSpinLockDemo.h"
#import <libkern/OSAtomic.h>
@interface OSSpinLockDemo()
@property (assign, nonatomic) OSSpinLock ticketLock;
@end
@implementation OSSpinLockDemo
- (instancetype)init
{
self = [super init];
if (self) {
self.ticketLock = OS_SPINLOCK_INIT;
}
return self;
}
//賣票
- (void)sellingTickets{
OSSpinLockLock(&_ticketLock);
[super sellingTickets];
OSSpinLockUnlock(&_ticketLock);
}
@end
複製程式碼
OSSpinLock
在iOS10.0以後就被棄用了,可以使用os_unfair_lock_lock
替代。而且還有一些安全性問題,具體參考不再安全的 OSSpinLock
2、os_unfair_lock
os_unfair_lock
用於取代不安全的OSSpinLock
,從iOS10開始才支援
從底層呼叫看,等待os_unfair_lock鎖的執行緒會處於休眠狀態,並非忙等
需要匯入標頭檔案#import <os/lock.h>
//初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
//加鎖
os_unfair_lock_lock(&lock);
//解鎖
os_unfair_lock_unlock(&lock);
複製程式碼
demo
#import "os_unfair_lockDemo.h"
#import <os/lock.h>
@interface os_unfair_lockDemo()
@property (assign, nonatomic) os_unfair_lock ticketLock;
@end
@implementation os_unfair_lockDemo
- (instancetype)init
{
self = [super init];
if (self) {
self.ticketLock = OS_UNFAIR_LOCK_INIT;
}
return self;
}
//賣票
- (void)sellingTickets{
os_unfair_lock_lock(&_ticketLock);
[super sellingTickets];
os_unfair_lock_unlock(&_ticketLock);
}
@end
複製程式碼
3、pthread_mutex
mutex叫做”互斥鎖”,等待鎖的執行緒會處於休眠狀態。需要匯入標頭檔案#import <pthread.h> 使用步驟
- 1、初始化鎖的屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
/*
* Mutex type attributes
*/
#define PTHREAD_MUTEX_NORMAL 0
#define PTHREAD_MUTEX_ERRORCHECK 1
#define PTHREAD_MUTEX_RECURSIVE 2
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
複製程式碼
- 2、初始化鎖
// 初始化鎖
pthread_mutex_init(mutex, &attr);
複製程式碼
- 3、初始化鎖結束以後,銷燬屬性
// 銷燬屬性
pthread_mutexattr_destroy(&attr);
複製程式碼
- 4、加鎖解鎖
pthread_mutex_lock(&_mutex);
pthread_mutex_unlock(&_mutex);
複製程式碼
- 5、銷燬鎖
pthread_mutex_destroy(&_mutex);
複製程式碼
備註:我們可以不初始化屬性,在傳屬性的時候直接傳NULL
,表示使用預設屬性PTHREAD_MUTEX_NORMAL
。pthread_mutex_init(mutex, NULL);
具體程式碼
#import "pthread_mutexDemo.h"
#import <pthread.h>
@interface pthread_mutexDemo()
@property (assign, nonatomic) pthread_mutex_t ticketMutex;
@end
@implementation pthread_mutexDemo
- (instancetype)init
{
self = [super init];
if (self) {
// 初始化屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化鎖
pthread_mutex_init(&(_ticketMutex), &attr);
// 銷燬屬性
pthread_mutexattr_destroy(&attr);
}
return self;
}
//賣票
- (void)sellingTickets{
pthread_mutex_lock(&_ticketMutex);
[super sellingTickets];
pthread_mutex_unlock(&_ticketMutex);
}
@end
複製程式碼
死鎖 我們稍微的修改一下程式碼
//賣票
- (void)sellingTickets{
pthread_mutex_lock(&_ticketMutex);
[super sellingTickets];
[self sellingTickets2];
pthread_mutex_unlock(&_ticketMutex);
}
- (void)sellingTickets2{
pthread_mutex_lock(&_ticketMutex);
NSLog(@"%s",__func__);
pthread_mutex_unlock(&_ticketMutex);
}
複製程式碼
上面的程式碼就會造成執行緒死鎖
,因為方法sellingTickets
的結束需要sellingTickets2
解鎖,方法sellingTickets2
的結束需要sellingTickets
解鎖,相互引用造成死鎖
但是pthread_mutex_t
裡面有一個屬性可以解決這個問題PTHREAD_MUTEX_RECURSIVE
PTHREAD_MUTEX_RECURSIVE
遞迴鎖:允許同一個執行緒對同一把鎖進行重複加鎖。要考重點同一個執行緒
和同一把鎖
- (instancetype)init
{
self = [super init];
if (self) {
// 初始化屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 初始化鎖
pthread_mutex_init(&(_ticketMutex), &attr);
// 銷燬屬性
pthread_mutexattr_destroy(&attr);
}
return self;
}
複製程式碼
對於上面的問題還有一個解決方案就是在方法sellingTickets2
中重新在建立一把新的鎖,兩個方法的鎖物件不同,就不會造成執行緒死鎖了。
條件
// 初始化屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 初始化鎖
pthread_mutex_init(&_mutex, &attr);
// 銷燬屬性
pthread_mutexattr_destroy(&attr);
// 初始化條件
pthread_cond_t condition
pthread_cond_init(&_cond, NULL);
// 等待條件
pthread_cond_wait(&_cond, &_mutex);
//啟用一個等待該條件的執行緒
pthread_cond_signal(&_cond);
//啟用所有等待該條件的執行緒
pthread_cond_broadcast(&_cond);
//銷燬資源
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
複製程式碼
使用案例:假設我們有一個陣列,裡面有兩個執行緒,一個是新增陣列,一個是刪除陣列,我們先呼叫刪除陣列,在呼叫新增陣列,但是在陣列為空的時候不呼叫刪除陣列。
#import "pthread_mutexDemo1.h"
#import <pthread.h>
@interface pthread_mutexDemo1()
@property (assign, nonatomic) pthread_mutex_t mutex;
@property (assign, nonatomic) pthread_cond_t cond;
@property (strong, nonatomic) NSMutableArray *data;
@end
@implementation pthread_mutexDemo1
- (instancetype)init
{
if (self = [super init]) {
// 初始化屬性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 初始化鎖
pthread_mutex_init(&_mutex, &attr);
// 銷燬屬性
pthread_mutexattr_destroy(&attr);
// 初始化條件
pthread_cond_init(&_cond, NULL);
self.data = [NSMutableArray array];
}
return self;
}
- (void)otherTest
{
[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}
// 執行緒1
// 刪除陣列中的元素
- (void)__remove
{
pthread_mutex_lock(&_mutex);
NSLog(@"__remove - begin");
if (self.data.count == 0) {
// 等待
pthread_cond_wait(&_cond, &_mutex);
}
[self.data removeLastObject];
NSLog(@"刪除了元素");
pthread_mutex_unlock(&_mutex);
}
// 執行緒2
// 往陣列中新增元素
- (void)__add
{
pthread_mutex_lock(&_mutex);
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"新增了元素");
// 啟用一個等待該條件的執行緒
pthread_cond_signal(&_cond);
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
複製程式碼
為了準確測試我們可以在__add
中sleep(1)
4、NSLock
NSLock是對mutex
普通鎖的封裝。pthread_mutex_init(mutex, NULL);
NSLock 遵循 NSLocking 協議。Lock 方法是加鎖,unlock 是解鎖,tryLock 是嘗試加鎖,如果失敗的話返回 NO,lockBeforeDate: 是在指定Date之前嘗試加鎖,如果在指定時間之前都不能加鎖,則返回NO
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
@interface NSLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name
@end
複製程式碼
使用起來也是十分的簡單
#import "LockDemo.h"
@interface LockDemo()
@property (strong, nonatomic) NSLock *ticketLock;
@end
@implementation LockDemo
//賣票
- (void)sellingTickets{
[self.ticketLock lock];
[super sellingTickets];
[self.ticketLock unlock];
}
@end
複製程式碼
5、NSRecursiveLock
NSRecursiveLock是對mutex
遞迴鎖的封裝,API跟NSLock基本一致
#import "RecursiveLockDemo.h"
@interface RecursiveLockDemo()
@property (nonatomic,strong) NSRecursiveLock *ticketLock;
@end
@implementation RecursiveLockDemo
//賣票
- (void)sellingTickets{
[self.ticketLock lock];
[super sellingTickets];
[self.ticketLock unlock];
}
@end
複製程式碼
6、NSCondition
NSCondition是對mutex
和cond
的封裝,更加物件導向,我們使用起來也更加的方便簡潔
@interface NSCondition : NSObject <NSLocking> {
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
@property (nullable, copy) NSString *name
@end
複製程式碼
對於上面那個陣列操作的案例我們就可以變成這個樣子了
// 執行緒1
// 刪除陣列中的元素
- (void)__remove
{
[self.condition lock];
if (self.data.count == 0) {
// 等待
[self.condition wait];
}
[self.data removeLastObject];
NSLog(@"刪除了元素");
[self.condition unlock];
}
// 執行緒2
// 往陣列中新增元素
- (void)__add
{
[self.condition lock];
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"新增了元素");
// 訊號
[self.condition signal];
[self.condition unlock];
}
複製程式碼
7、NSConditionLock
NSConditionLock是對NSCondition的進一步封裝,可以設定具體的條件值
@interface NSConditionLock : NSObject <NSLocking> {
- (instancetype)initWithCondition:(NSInteger)condition;
@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name;
@end
複製程式碼
裡面有三個常用的方法
- 1、
initWithCondition:
初始化Condition
,並且設定狀態值 - 2、
lockWhenCondition:(NSInteger)condition:
當狀態值為condition的時候加鎖 - 3、
unlockWithCondition:(NSInteger)condition
當狀態值為condition的時候解鎖
@interface NSConditionLockDemo()
@property (strong, nonatomic) NSConditionLock *conditionLock;
@end
@implementation NSConditionLockDemo
- (instancetype)init
{
if (self = [super init]) {
self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
}
return self;
}
- (void)otherTest
{
[[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];
}
- (void)__one
{
[self.conditionLock lock];
NSLog(@"__one");
sleep(1);
[self.conditionLock unlockWithCondition:2];
}
- (void)__two
{
[self.conditionLock lockWhenCondition:2];
NSLog(@"__two");
[self.conditionLock unlockWithCondition:3];
}
@end
複製程式碼
8、dispatch_semaphore
- semaphore叫做”訊號量”
- 訊號量的初始值,可以用來控制執行緒併發訪問的最大數量
- 訊號量的初始值為1,代表同時只允許1條執行緒訪問資源,保證執行緒同步
//表示最多開啟5個執行緒
dispatch_semaphore_create(5);
// 如果訊號量的值 > 0,就讓訊號量的值減1,然後繼續往下執行程式碼
// 如果訊號量的值 <= 0,就會休眠等待,直到訊號量的值變成>0,就讓訊號量的值減1,然後繼續往下執行程式碼
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
// 讓訊號量的值+1
dispatch_semaphore_signal(self.semaphore);
複製程式碼
@interface dispatch_semaphoreDemo()
@property (strong, nonatomic) dispatch_semaphore_t semaphore;
@end
@implementation dispatch_semaphoreDemo
- (instancetype)init
{
if (self = [super init]) {
self.semaphore = dispatch_semaphore_create(1);
}
return self;
}
- (void)otherTest
{
for (int i = 0; i < 20; i++) {
[[[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil] start];
}
}
- (void)test
{
// 如果訊號量的值 > 0,就讓訊號量的值減1,然後繼續往下執行程式碼
// 如果訊號量的值 <= 0,就會休眠等待,直到訊號量的值變成>0,就讓訊號量的值減1,然後繼續往下執行程式碼
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
sleep(2);
NSLog(@"test - %@", [NSThread currentThread]);
// 讓訊號量的值+1
dispatch_semaphore_signal(self.semaphore);
}
@end
複製程式碼
我們在執行程式碼列印的時候發現,每隔一秒出現一次列印。雖然我們同時開啟20個執行緒,但是一次只能訪問一條執行緒的資源
9、dispatch_queue
使用GCD的序列佇列也可以實現執行緒同步的
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
// 追加任務1
for (int i = 0; i < 2; ++i) {
NSLog(@"1---%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
// 追加任務2
for (int i = 0; i < 2; ++i) {
NSLog(@"2---%@",[NSThread currentThread]);
}
});
複製程式碼
10、@synchronized
@synchronized
是對mutex
遞迴鎖的封裝,
@synchronized(obj)
內部會生成obj對應的遞迴鎖,然後進行加鎖、解鎖操作
//賣票
- (void)sellingTickets{
@synchronized ([self class]) {
[super sellingTickets];
}
}
複製程式碼
對是實現底層我們可以在objc4的objc-sync.mm檔案中找到
synchronized
就是在開始和結束的時候呼叫了objc_sync_enter
&objc_sync_exit
方法。
objc_sync_enter
實現
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
SyncData* data = id2data(obj, ACQUIRE);
assert(data);
data->mutex.lock();
} else {
// @synchronized(nil) does nothing
if (DebugNilSync) {
_objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
}
objc_sync_nil();
}
return result;
}
複製程式碼
就是根據id2data
方法找到一個data
物件,然後在對data
物件進行mutex.lock()
加鎖操作。我們點選進入id2data
方法繼續查詢
#define LIST_FOR_OBJ(obj) sDataLists[obj].data
static StripedMap<SyncList> sDataLists;
複製程式碼
發現獲取data
物件的方法其實就是根據sDataLists[obj].data
這個方法來實現的,也就是一個雜湊表。
11、atomic
- atomic用於保證屬性setter、getter的原子性操作,相當於在getter和setter內部加了執行緒同步的鎖
- 可以參考原始碼objc4的objc-accessors.mm
- 它並不能保證使用屬性的過程是執行緒安全的
12、pthread_rwlock:讀寫鎖
pthread_rwlock經常用於檔案等資料的讀寫操作,需要匯入標頭檔案#import <pthread.h>
iOS中的讀寫安全方案需要注意一下場景
- 1、同一時間,只能有1個執行緒進行寫的操作
- 2、同一時間,允許有多個執行緒進行讀的操作
- 3、同一時間,不允許既有寫的操作,又有讀的操作
//初始化鎖
pthread_rwlock_t lock;
pthread_rwlock_init(&_lock, NULL);
//讀加鎖
pthread_rwlock_rdlock(&_lock);
//讀嘗試加鎖
pthread_rwlock_trywrlock(&_lock)
//寫加鎖
pthread_rwlock_wrlock(&_lock);
//寫嘗試加鎖
pthread_rwlock_trywrlock(&_lock)
//解鎖
pthread_rwlock_unlock(&_lock);
//銷燬
pthread_rwlock_destroy(&_lock);
複製程式碼
#import <pthread.h>
@interface pthread_rwlockDemo ()
@property (assign, nonatomic) pthread_rwlock_t lock;
@end
@implementation pthread_rwlockDemo
- (instancetype)init
{
self = [super init];
if (self) {
// 初始化鎖
pthread_rwlock_init(&_lock, NULL);
}
return self;
}
- (void)otherTest{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
[self read];
});
dispatch_async(queue, ^{
[self write];
});
}
}
- (void)read {
pthread_rwlock_rdlock(&_lock);
sleep(1);
NSLog(@"%s", __func__);
pthread_rwlock_unlock(&_lock);
}
- (void)write
{
pthread_rwlock_wrlock(&_lock);
sleep(1);
NSLog(@"%s", __func__);
pthread_rwlock_unlock(&_lock);
}
- (void)dealloc
{
pthread_rwlock_destroy(&_lock);
}
@end
複製程式碼
我們可以發現讀操作1s有可能出現多次,但是寫操作不會
13、dispatch_barrier_async
這個函式傳入的併發佇列必須是自己通過dispatch_queue_cretate建立的 如果傳入的是一個序列或是一個全域性的併發佇列,那這個函式便等同於dispatch_async函式的效果
//初始化
self.queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
//讀操作
dispatch_async(self.queue, ^{
});
//寫操作
dispatch_barrier_async(self.queue, ^{
});
複製程式碼
鎖的效能比較
效能從高到低排序
- 1、os_unfair_lock
- 2、OSSpinLock
- 3、dispatch_semaphore
- 4、pthread_mutex
- 5、dispatch_queue(DISPATCH_QUEUE_SERIAL)
- 6、NSLock
- 7、NSCondition
- 8、pthread_mutex(recursive)
- 9、NSRecursiveLock
- 10、NSConditionLock
- 11、@synchronized