聲明:本文主要參考文章iOS多線程安全-13種線程鎖,整理了代碼格式,看起來更舒服一些
- 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、鎖的性能比較
為什么要線程安全
多個(gè)線程訪問同一塊資源的時(shí)候,很容易引發(fā)數(shù)據(jù)混亂問題。 一個(gè)大家都喜歡拿來舉例子的就是買票demo,今天我使用這個(gè)案例 假設(shè)有100張票,同時(shí)開5個(gè)窗口買票,5個(gè)窗口買票,我們來看看結(jié)果
//賣票演示
- (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āng)前剩余票數(shù)-> %d", oldMoney);
}

正常情況下我有50張票,然后賣了50次,剩余票數(shù)應(yīng)該是0,但是打印結(jié)果竟然是3,所以這里就存在了線程安全問題。
出現(xiàn)線程安全的原因

出現(xiàn)線程安全的原因就是在同一個(gè)時(shí)間,多個(gè)線程同時(shí)讀取一個(gè)值,像線程A和B同時(shí)讀取了當(dāng)前票數(shù)為10,等于是賣了兩張票,但是總票數(shù)其實(shí)就減少了一張。
解決方法
使用線程同步技術(shù),按照預(yù)定的先后次序依次進(jìn)行,常見的線程同步技術(shù)就是加鎖

自旋鎖和互斥鎖
自旋鎖(Spin lock)
自旋鎖與互斥鎖有點(diǎn)類似,只是自旋鎖不會(huì)引起調(diào)用者睡眠,如果自旋鎖已經(jīng)被別的執(zhí)行單元保持,調(diào)用者就一直循環(huán)在那里看是 否該自旋鎖的保持者已經(jīng)釋放了鎖,"自旋"一詞就是因此而得名。其作用是為了解決某項(xiàng)資源的互斥使用。因?yàn)樽孕i不會(huì)引起調(diào)用者睡眠,所以自旋鎖的效率遠(yuǎn) 高于互斥鎖。雖然它的效率比互斥鎖高,但是它也有些不足之處: 1、自旋鎖一直占用CPU,他在未獲得鎖的情況下,一直運(yùn)行--自旋,所以占用著CPU,如果不能在很短的時(shí) 間內(nèi)獲得鎖,這無疑會(huì)使CPU效率降低。 2、在用自旋鎖時(shí)有可能造成死鎖,當(dāng)遞歸調(diào)用時(shí)有可能造成死鎖,調(diào)用有些其他函數(shù)也可能造成死鎖,如 copy_to_user()、copy_from_user()、kmalloc()等。 因此我們要慎重使用自旋鎖,自旋鎖只有在內(nèi)核可搶占式或SMP的情況下才真正需要,在單CPU且不可搶占式的內(nèi)核下,自旋鎖的操作為空操作。自旋鎖適用于鎖使用者保持鎖時(shí)間比較短的情況下。
互斥鎖
互斥鎖屬于sleep-waiting類型的鎖。例如在一個(gè)雙核的機(jī)器上有兩個(gè)線程(線程A和線程B),它們分別運(yùn)行在Core0和 Core1上。假設(shè)線程A想要通過pthread_mutex_lock操作去得到一個(gè)臨界區(qū)的鎖,而此時(shí)這個(gè)鎖正被線程B所持有,那么線程A就會(huì)被阻塞 (blocking),Core0 會(huì)在此時(shí)進(jìn)行上下文切換(Context Switch)將線程A置于等待隊(duì)列中,此時(shí)Core0就可以運(yùn)行其他的任務(wù)(例如另一個(gè)線程C)而不必進(jìn)行忙等待。而自旋鎖則不然,它屬于busy-waiting類型的鎖,如果線程A是使用pthread_spin_lock操作去請(qǐng)求鎖,那么線程A就會(huì)一直在 Core0上進(jìn)行忙等待并不停的進(jìn)行鎖請(qǐng)求,直到得到這個(gè)鎖為止。
兩種鎖的加鎖原理
互斥鎖:線程會(huì)從sleep(加鎖)——>running(解鎖),過程中有上下文的切換,cpu的搶占,信號(hào)的發(fā)送等開銷。
自旋鎖:線程一直是running(加鎖——>解鎖),死循環(huán)檢測(cè)鎖的標(biāo)志位,機(jī)制不復(fù)雜。
對(duì)比 互斥鎖的起始原始開銷要高于自旋鎖,但是基本是一勞永逸,臨界區(qū)持鎖時(shí)間的大小并不會(huì)對(duì)互斥鎖的開銷造成影響,而自旋鎖是死循環(huán)檢測(cè),加鎖全程消耗cpu,起始開銷雖然低于互斥鎖,但是隨著持鎖時(shí)間,加鎖的開銷是線性增長(zhǎng)。
兩種鎖的應(yīng)用
互斥鎖用于臨界區(qū)持鎖時(shí)間比較長(zhǎng)的操作,比如下面這些情況都可以考慮
- 1 臨界區(qū)有IO操作
- 2 臨界區(qū)代碼復(fù)雜或者循環(huán)量大
- 3 臨界區(qū)競(jìng)爭(zhēng)非常激烈
- 4 單核處理器
至于自旋鎖就主要用在臨界區(qū)持鎖時(shí)間非常短且CPU資源不緊張的情況下,自旋鎖一般用于多核的服務(wù)器。
13種鎖
1、OSSpinLock
OSSpinLock叫做”自旋鎖”,使用時(shí)需要導(dǎo)入頭文件#import <libkern/OSAtomic.h>
//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
//加鎖
OSSpinLockLock(&lock);
//解鎖
OSSpinLockUnlock(&lock);
復(fù)制代碼
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開始才支持 從底層調(diào)用看,等待os_unfair_lock鎖的線程會(huì)處于休眠狀態(tài),并非忙等 需要導(dǎo)入頭文件#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叫做”互斥鎖”,等待鎖的線程會(huì)處于休眠狀態(tài)。需要導(dǎo)入頭文件#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、初始化鎖結(jié)束以后,銷毀屬性
// 銷毀屬性
pthread_mutexattr_destroy(&attr);
- 4、加鎖解鎖
pthread_mutex_lock(&_mutex);
pthread_mutex_unlock(&_mutex);
- 5、銷毀鎖
pthread_mutex_destroy(&_mutex);
備注:我們可以不初始化屬性,在傳屬性的時(shí)候直接傳NULL,表示使用默認(rèn)屬性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);
}

上面的代碼就會(huì)造成線程死鎖,因?yàn)榉椒?code>sellingTickets的結(jié)束需要sellingTickets2解鎖,方法sellingTickets2的結(jié)束需要sellingTickets解鎖,相互引用造成死鎖
但是pthread_mutex_t里面有一個(gè)屬性可以解決這個(gè)問題PTHREAD_MUTEX_RECURSIVE
PTHREAD_MUTEX_RECURSIVE 遞歸鎖:允許同一個(gè)線程對(duì)同一把鎖進(jìn)行重復(fù)加鎖。要考重點(diǎn)同一個(gè)線程和同一把鎖
- (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;
}
對(duì)于上面的問題還有一個(gè)解決方案就是在方法sellingTickets2中重新在創(chuàng)建一把新的鎖,兩個(gè)方法的鎖對(duì)象不同,就不會(huì)造成線程死鎖了。

條件
// 初始化屬性
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);
//激活一個(gè)等待該條件的線程
pthread_cond_signal(&_cond);
//激活所有等待該條件的線程
pthread_cond_broadcast(&_cond);
//銷毀資源
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
使用案例:假設(shè)我們有一個(gè)數(shù)組,里面有兩個(gè)線程,一個(gè)是添加數(shù)組,一個(gè)是刪除數(shù)組,我們先調(diào)用刪除數(shù)組,在調(diào)用添加數(shù)組,但是在數(shù)組為空的時(shí)候不調(diào)用刪除數(shù)組。
#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
// 刪除數(shù)組中的元素
- (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
// 往數(shù)組中添加元素
- (void)__add
{
pthread_mutex_lock(&_mutex);
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 激活一個(gè)等待該條件的線程
pthread_cond_signal(&_cond);
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
為了準(zhǔn)確測(cè)試我們可以在__add中sleep(1)

4、NSLock
NSLock是對(duì)mutex普通鎖的封裝。pthread_mutex_init(mutex, NULL);
NSLock 遵循 NSLocking 協(xié)議。Lock 方法是加鎖,unlock 是解鎖,tryLock 是嘗試加鎖,如果失敗的話返回 NO,lockBeforeDate: 是在指定Date之前嘗試加鎖,如果在指定時(shí)間之前都不能加鎖,則返回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
使用起來也是十分的簡(jiǎn)單
#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是對(duì)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是對(duì)mutex和cond的封裝,更加面向?qū)ο?,我們使用起來也更加的方便?jiǎn)潔
@interface NSCondition : NSObject <NSLocking> {
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
@property (nullable, copy) NSString *name
@end
復(fù)制代碼
對(duì)于上面那個(gè)數(shù)組操作的案例我們就可以變成這個(gè)樣子了
// 線程1
// 刪除數(shù)組中的元素
- (void)__remove
{
[self.condition lock];
if (self.data.count == 0) {
// 等待
[self.condition wait];
}
[self.data removeLastObject];
NSLog(@"刪除了元素");
[self.condition unlock];
}
// 線程2
// 往數(shù)組中添加元素
- (void)__add
{
[self.condition lock];
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 信號(hào)
[self.condition signal];
[self.condition unlock];
}
7、NSConditionLock
NSConditionLock是對(duì)NSCondition的進(jìn)一步封裝,可以設(shè)置具體的條件值
@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
復(fù)制代碼
里面有三個(gè)常用的方法
- 1、
initWithCondition:初始化Condition,并且設(shè)置狀態(tài)值 - 2、
lockWhenCondition:(NSInteger)condition:當(dāng)狀態(tài)值為condition的時(shí)候加鎖 - 3、
unlockWithCondition:(NSInteger)condition當(dāng)狀態(tài)值為condition的時(shí)候解鎖
@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叫做”信號(hào)量”
- 信號(hào)量的初始值,可以用來控制線程并發(fā)訪問的最大數(shù)量
- 信號(hào)量的初始值為1,代表同時(shí)只允許1條線程訪問資源,保證線程同步
//表示最多開啟5個(gè)線程
dispatch_semaphore_create(5);
// 如果信號(hào)量的值 > 0,就讓信號(hào)量的值減1,然后繼續(xù)往下執(zhí)行代碼
// 如果信號(hào)量的值 <= 0,就會(huì)休眠等待,直到信號(hào)量的值變成>0,就讓信號(hào)量的值減1,然后繼續(xù)往下執(zhí)行代碼
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
// 讓信號(hào)量的值+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
{
// 如果信號(hào)量的值 > 0,就讓信號(hào)量的值減1,然后繼續(xù)往下執(zhí)行代碼
// 如果信號(hào)量的值 <= 0,就會(huì)休眠等待,直到信號(hào)量的值變成>0,就讓信號(hào)量的值減1,然后繼續(xù)往下執(zhí)行代碼
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
sleep(2);
NSLog(@"test - %@", [NSThread currentThread]);
// 讓信號(hào)量的值+1
dispatch_semaphore_signal(self.semaphore);
}
@end
我們?cè)谶\(yùn)行代碼打印的時(shí)候發(fā)現(xiàn),每隔一秒出現(xiàn)一次打印。雖然我們同時(shí)開啟20個(gè)線程,但是一次只能訪問一條線程的資源
9、dispatch_queue
使用GCD的串行隊(duì)列也可以實(shí)現(xiàn)線程同步的
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
// 追加任務(wù)1
for (int i = 0; i < 2; ++i) {
NSLog(@"1---%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
// 追加任務(wù)2
for (int i = 0; i < 2; ++i) {
NSLog(@"2---%@",[NSThread currentThread]);
}
});
10、@synchronized
@synchronized是對(duì)mutex遞歸鎖的封裝, @synchronized(obj)內(nèi)部會(huì)生成obj對(duì)應(yīng)的遞歸鎖,然后進(jìn)行加鎖、解鎖操作
//賣票
- (void)sellingTickets{
@synchronized ([self class]) {
[super sellingTickets];
}
}
對(duì)是實(shí)現(xiàn)底層我們可以在objc4的objc-sync.mm文件中找到 synchronized就是在開始和結(jié)束的時(shí)候調(diào)用了objc_sync_enter&objc_sync_exit方法。
objc_sync_enter實(shí)現(xiàn)
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;
}
就是根據(jù)id2data方法找到一個(gè)data對(duì)象,然后在對(duì)data對(duì)象進(jìn)行mutex.lock()加鎖操作。我們點(diǎn)擊進(jìn)入id2data方法繼續(xù)查找
#define LIST_FOR_OBJ(obj) sDataLists[obj].data
static StripedMap<SyncList> sDataLists;
發(fā)現(xiàn)獲取data對(duì)象的方法其實(shí)就是根據(jù)sDataLists[obj].data這個(gè)方法來實(shí)現(xiàn)的,也就是一個(gè)哈希表。
關(guān)于 @synchronized,這兒比你想知道的還要多
11、atomic
- atomic用于保證屬性setter、getter的原子性操作,相當(dāng)于在getter和setter內(nèi)部加了線程同步的鎖
- 可以參考源碼objc4的objc-accessors.mm
- 它并不能保證使用屬性的過程是線程安全的
12、pthread_rwlock:讀寫鎖
pthread_rwlock經(jīng)常用于文件等數(shù)據(jù)的讀寫操作,需要導(dǎo)入頭文件#import <pthread.h>
iOS中的讀寫安全方案需要注意一下場(chǎng)景
- 1、同一時(shí)間,只能有1個(gè)線程進(jìn)行寫的操作
- 2、同一時(shí)間,允許有多個(gè)線程進(jìn)行讀的操作
- 3、同一時(shí)間,不允許既有寫的操作,又有讀的操作
//初始化鎖
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

我們可以發(fā)現(xiàn)讀操作1s有可能出現(xiàn)多次,但是寫操作不會(huì)
13、dispatch_barrier_async
這個(gè)函數(shù)傳入的并發(fā)隊(duì)列必須是自己通過dispatch_queue_cretate創(chuàng)建的 如果傳入的是一個(gè)串行或是一個(gè)全局的并發(fā)隊(duì)列,那這個(gè)函數(shù)便等同于dispatch_async函數(shù)的效果
//初始化
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