iOS開發(fā)常用線程安全鎖
正文
多線程開發(fā),就會(huì)有資源搶占的情況,導(dǎo)致出現(xiàn)我們意想不到的數(shù)據(jù)問題,我們就需要對數(shù)據(jù)進(jìn)行加鎖,已保證線程安全.
鎖主要分為兩大類自旋鎖和互斥鎖。
- 自旋鎖:自旋鎖已經(jīng)被別的執(zhí)行單元保持,調(diào)用者就一直循環(huán)在那里看是否該自旋鎖的保持者已經(jīng)釋放了鎖,因此是一種忙等待。自旋鎖避免了線程上下文切換的調(diào)度開銷,因此對于線程只會(huì)阻塞很短的時(shí)間是很高效的,但是對于比較長時(shí)間的阻塞也是比較消耗CPU的。(線程忙等)
- 互斥鎖:如果資源已經(jīng)被占用,資源申請者只能進(jìn)入睡眠狀態(tài)。有上下文的切換(主動(dòng)出讓時(shí)間片, 線程休眠, 等待下一次喚醒)、CPU的搶占、信號(hào)的發(fā)送等開銷。(線程閑等)
原子屬性
我們創(chuàng)建屬性一般都會(huì)設(shè)置屬性為非原子屬性noatomic, 因?yàn)樵訉傩詀tomic會(huì)有額外的加鎖開銷,那如果我們創(chuàng)建屬性使用原子屬性atomic,它能保證property是線程安全的嗎?
#import "ViewController.h"
@interface ViewController ()
@property (atomic ,assign) int count;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.count = 0;
[self test_atomic];
}
- (void)test_atomic {
// self.count初始值是10
for (int i = 0; i < 10; i ++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.count ++;
NSLog(@"%d",self.count);
});
}
}
@end

從上面我們可以看到原子屬性atomic不能保證數(shù)據(jù)的線程安全.下面我們從源碼進(jìn)行分析:在屬性的getter/setter方法調(diào)用的底層atomic和nonatomic有什么區(qū)別。先看看setter方法:objc_setProperty
void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) {
bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY);
bool mutableCopy = (shouldCopy == MUTABLE_COPY);
reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);
}
void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset{
reallySetProperty(self, _cmd, newValue, offset, true, false, false);
}
void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset) {
reallySetProperty(self, _cmd, newValue, offset, false, false, false);
}
void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) {
reallySetProperty(self, _cmd, newValue, offset, true, true, false);
}
void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset) {
reallySetProperty(self, _cmd, newValue, offset, false, true, false);
}
我們可以看到都是調(diào)用的reallySetProperty方法,atomic第五個(gè)參數(shù)為true,nonatomic為false, copy第六個(gè)參數(shù)為true, mutableCopy第七個(gè)參數(shù)為true.
static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) {
if (offset == 0) {
object_setClass(self, newValue);
return;
}
id oldValue;
id *slot = (id*) ((char*)self + offset);
if (copy) {
newValue = [newValue copyWithZone:nil];
} else if (mutableCopy) {
newValue = [newValue mutableCopyWithZone:nil];
} else {
if (*slot == newValue) return;
newValue = objc_retain(newValue);
}
if (!atomic) {
oldValue = *slot;
*slot = newValue;
} else {
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
oldValue = *slot;
*slot = newValue;
slotlock.unlock();
}
objc_release(oldValue);
}
copy和mutableCopy使用copyWithZone進(jìn)行新值的copy,其他使用objc_retain增加引用計(jì)數(shù).nonatomic直接進(jìn)行賦值;atomic會(huì)使用spinlock_t在賦值之前加鎖,賦值之后解鎖. 我們再來看看getter方法:objc_getProperty
id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
if (offset == 0) {
return object_getClass(self);
}
// Retain release world
id *slot = (id*) ((char*)self + offset);
if (!atomic) return *slot;
// Atomic retain release world
spinlock_t& slotlock = PropertyLocks[slot];
slotlock.lock();
id value = objc_retain(*slot);
slotlock.unlock();
// for performance, we (safely) issue the autorelease OUTSIDE of the spinlock.
return objc_autoreleaseReturnValue(value);
}
我們可以看到nonatomic直接返回,atomic在取值前加鎖,取值后解鎖,再返回值.
那么原子屬性atomic在getter/setter底層有加鎖解鎖操作,為什么不能保證線程安全的呢?
因?yàn)樵訉傩詀tomic鎖住資源的范圍不夠大。在self.count --;的時(shí)候,既有g(shù)etter也有setter,可能就出現(xiàn)當(dāng)getter的時(shí)候還沒有return出去就被其它線程setter。
OSSpinLock - 自旋鎖
OSSpinLock 在iOS10之后被移除了。 被移除的原因是它有一個(gè)bug:優(yōu)先級反轉(zhuǎn)。
優(yōu)先級反轉(zhuǎn):當(dāng)多個(gè)線程有優(yōu)先級的時(shí)候,有一個(gè)優(yōu)先級較低的線程先去訪問了資源,并是有了OSSpinLock對資源加鎖,又來一個(gè)優(yōu)先級較高的線程去訪問了這個(gè)資源,這個(gè)時(shí)候優(yōu)先級較高的線程就會(huì)一直占用cpu的資源,導(dǎo)致優(yōu)先級較低的線程沒辦法與較高的線程爭奪cpu的時(shí)間,最后導(dǎo)致最先被優(yōu)先級較低的線程鎖住的資源遲遲不能被釋放,從而造成優(yōu)先級反轉(zhuǎn)的bug。
所以 OSSpinLock使用限制:必須保證所有訪問同一資源的線程處于優(yōu)先級平等的時(shí)候,才可以使用。
OSSpinLock已被蘋果放棄了,大家也可以放棄它,蘋果設(shè)計(jì)了os_unfair_lock來代替OSSpinLock。
os_unfair_lock - 互斥鎖
iOS10之后開始支持,os_unfair_lock 在os庫中,使用之前需要導(dǎo)入頭文件<os/lock.h>。
#import "ViewController.h"
#import <os/lock.h>
@interface ViewController ()
@property (nonatomic ,assign) int count;
@property (nonatomic ,assign) os_unfair_lock unfairLock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.count = 0;
self.unfairLock = OS_UNFAIR_LOCK_INIT; // 初始化鎖
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
for (int i = 0; i<10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
os_unfair_lock_lock(&_unfairLock); // 加鎖
self.count ++;
NSLog(@"%d",self.count);
os_unfair_lock_unlock(&_unfairLock); // 解鎖
});
}
}
@end
NSLock - 互斥鎖
NSLock - Foundation框架內(nèi)部的??,使用起來非常方便,基于pthroad_mutex封裝而來,是一把互斥非遞歸鎖。因?yàn)镺C的Foundation框架是非開源的,所以我們查看swift的Foundation框架,來查看其源碼實(shí)現(xiàn),原理是相同的
#if os(Windows)
#elseif CYGWIN
#else
private typealias _MutexPointer = UnsafeMutablePointer<pthread_mutex_t>
private typealias _RecursiveMutexPointer = UnsafeMutablePointer<pthread_mutex_t>
private typealias _ConditionVariablePointer = UnsafeMutablePointer<pthread_cond_t>
#endif
open class NSLock: NSObject, NSLocking {
internal var mutex = _MutexPointer.allocate(capacity: 1)
#if os(macOS) || os(iOS) || os(Windows)
private var timeoutCond = _ConditionVariablePointer.allocate(capacity: 1)
private var timeoutMutex = _MutexPointer.allocate(capacity: 1)
#endif
public override init() {
#if os(Windows)
#else
pthread_mutex_init(mutex, nil)
#if os(macOS) || os(iOS)
pthread_cond_init(timeoutCond, nil)
pthread_mutex_init(timeoutMutex, nil)
#endif
#endif
}
deinit {
#if os(Windows)
#else
pthread_mutex_destroy(mutex)
#endif
mutex.deinitialize(count: 1)
mutex.deallocate()
#if os(macOS) || os(iOS) || os(Windows)
deallocateTimedLockData(cond: timeoutCond, mutex: timeoutMutex)
#endif
}
open func lock() {
#if os(Windows)
#else
pthread_mutex_lock(mutex)
#endif
}
open func unlock() {
#if os(Windows)
#else
pthread_mutex_unlock(mutex)
#if os(macOS) || os(iOS)
// Wakeup any threads waiting in lock(before:)
pthread_mutex_lock(timeoutMutex)
pthread_cond_broadcast(timeoutCond)
pthread_mutex_unlock(timeoutMutex)
#endif
#endif
}
...
}
我們可以起內(nèi)部是對pthread_mutex_t的封裝
- 構(gòu)造方法 init()就是調(diào)用了pthread的pthread_mutex_init(mutex, nil)方法
- 析構(gòu)方法 deinit就是調(diào)用了pthread的pthread_mutex_destroy(mutex)方法
- 加鎖方法 lock()就是調(diào)用了pthread的pthread_mutex_lock(mutex)方法
- 解鎖方法 unlock()就是調(diào)用了pthread的pthread_mutex_unlock(mutex)方法
在pthread_mutex中可以通過pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))來設(shè)置鎖為遞歸鎖,這里并沒有設(shè)置,所以NSLock不是一把遞歸鎖!
NSCondition - 互斥鎖
我們通過查看swift foundation 源碼 可以看到其和NSLock類似,也是對pthread_mutex_t的封裝,相比于NSLock,NSCondition多了幾個(gè)API:
open func wait() {
pthread_cond_wait(cond, mutex)
}
open func wait(until limit: Date) -> Bool {
guard var timeout = timeSpecFrom(date: limit) else {
return false
}
return pthread_cond_timedwait(cond, mutex, &timeout) == 0
}
open func signal() {
pthread_cond_signal(cond)
}
open func broadcast() {
pthread_cond_broadcast(cond)
}
- (void)wait 阻塞當(dāng)前線程,使線程進(jìn)入休眠,等待喚醒信號(hào)。調(diào)用前必須已加鎖。
- (void)waitUntilDate 阻塞當(dāng)前線程,使線程進(jìn)入休眠,等待喚醒信號(hào)或者超時(shí)。調(diào)用前必須已加鎖。
- (void)signal 喚醒一個(gè)正在休眠的線程,如果要喚醒多個(gè),需要調(diào)用多次。如果沒有線程在等待,則什么也不做。調(diào)用前必須已加鎖。
- (void)broadcast 喚醒所有在等待的線程。如果沒有線程在等待,則什么也不做。調(diào)用前必須已加鎖。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic ,assign) int count;
@property (nonatomic ,strong) NSCondition *iCondition;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.count = 0;
self.iCondition = [[NSCondition alloc] init]; // 初始化鎖
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self nscondition_test];
}
#pragma mark -- NSCondition
- (void)nscondition_test {
// 生產(chǎn)
for (int i = 0; i < 50; i ++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self my_production];
});
}
// 消費(fèi)
for (int i = 0; i < 100; i ++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self my_consumption];
});
}
}
- (void)my_production {
[self.iCondition lock];
self.count ++;
NSLog(@"生產(chǎn)了一個(gè)產(chǎn)品,現(xiàn)有產(chǎn)品 : %d個(gè)",self.count);
[self.iCondition signal]; // 喚醒一個(gè)wait正在休眠的線程
[self.iCondition unlock];
}
- (void)my_consumption {
[self.iCondition lock];
while (self.count == 0) { // 這里使用 if 會(huì)出現(xiàn)現(xiàn)有產(chǎn)品是負(fù)數(shù)的情況
[self.iCondition wait]; // 阻塞當(dāng)前線程,使線程進(jìn)入休眠,等待喚醒信號(hào)signal
}
self.count --;
NSLog(@"消費(fèi)了一個(gè)產(chǎn)品,現(xiàn)有產(chǎn)品: %d個(gè)",self.count);
[self.iCondition unlock];
}
@end
注意??:pthread_mutex 存在虛假喚醒的情況,一個(gè)signl喚醒多個(gè)wait,不是預(yù)期的signal : wait = 1:1效果。 在編碼過程中可以通過while條件判斷,使被喚醒的線程,陷入while循環(huán)中,從而解決此問題。
NSConditionLock - 互斥鎖
NSConditionLock是基于NSCondition的封裝。目的是讓NSConditionLock自帶條件探測
open class NSConditionLock : NSObject, NSLocking {
internal var _cond = NSCondition()
......
open func lock(whenCondition condition: Int) {
let _ = lock(whenCondition: condition, before: Date.distantFuture)
}
open func `try`() -> Bool {
return lock(before: Date.distantPast)
}
open func tryLock(whenCondition condition: Int) -> Bool {
return lock(whenCondition: condition, before: Date.distantPast)
}
open func unlock(withCondition condition: Int) {
_cond.lock()
_thread = nil
_value = condition
_cond.broadcast()
_cond.unlock()
}
...
}
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic ,strong) NSConditionLock *iConditionLock;
@end
@implementation ViewController
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self conditonLock_test];
}
#pragma mark -- NSConditionLock
- (void)conditonLock_test {
self.iConditionLock = [[NSConditionLock alloc] initWithCondition:3];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self.iConditionLock lockWhenCondition:3];
NSLog(@"1");
[self.iConditionLock unlockWithCondition:2];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self.iConditionLock lockWhenCondition:2];
NSLog(@"2");
[self.iConditionLock unlockWithCondition:1];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self.iConditionLock lockWhenCondition:1];
NSLog(@"3");
[self.iConditionLock unlockWithCondition:0];
});
}
@end
// 線程任務(wù)的執(zhí)行順序:1 2 3
NSConditionLock能夠達(dá)到控制線程執(zhí)行任務(wù)順序的目的。
NSRecursiveLock
遞歸鎖:同一時(shí)刻只能被一條線程所擁有。 NSRecursiveLock是基于pthread的封裝,并設(shè)置了遞歸屬性。
open class NSRecursiveLock: NSObject, NSLocking {
internal var mutex = _RecursiveMutexPointer.allocate(capacity: 1)
#if os(macOS) || os(iOS) || os(Windows)
private var timeoutCond = _ConditionVariablePointer.allocate(capacity: 1)
private var timeoutMutex = _MutexPointer.allocate(capacity: 1)
#endif
public override init() {
super.init()
#if CYGWIN
var attrib : pthread_mutexattr_t? = nil
#else
var attrib = pthread_mutexattr_t()
#endif
withUnsafeMutablePointer(to: &attrib) { attrs in
pthread_mutexattr_init(attrs)
// 設(shè)置遞歸屬性
pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))
pthread_mutex_init(mutex, attrs)
}
pthread_cond_init(timeoutCond, nil)
}
NSConditionLock是一把遞歸鎖,可遞歸加鎖解鎖(可適用于遞歸函數(shù))
通過PTHREAD_MUTEX_RECURSIVE來設(shè)置鎖為遞歸鎖。當(dāng)鎖為遞歸鎖的時(shí)候,它的使用場景為單個(gè)線程中的遞歸調(diào)用。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic ,assign) int count;
@property (nonatomic ,strong) NSRecursiveLock *iRecursiveLock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.count = 0;
self.iRecursiveLock = [[NSRecursiveLock alloc] init]; // 初始化鎖
[self recursiveTest]; // 遞歸鎖案例
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
for (int i = 0; i<10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self recursiveLock_test];
});
}
}
#pragma mark -- NSRecursiveLock
-(void)recursiveLock_test {
[self.iRecursiveLock lock];
self.count ++;
NSLog(@"%d",self.count);
[self.iRecursiveLock unlock];
}
- (void)recursiveTest {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^recursiveMethod)(int);
recursiveMethod = ^(int value){
if (value > 0) {
[self.iRecursiveLock lock];
NSLog(@"%d",value);
recursiveMethod(value - 1);
[self.iRecursiveLock unlock];
}
};
recursiveMethod(10);
});
}
@end
如果在不同線程進(jìn)行遞歸調(diào)用的話,會(huì)出現(xiàn)問題,把recursiveTest方法放到for循環(huán)里
- (void)recursiveTest {
for (int i = 0; i < 5; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^recursiveMethod)(int);
recursiveMethod = ^(int value){
if (value > 0) {
[self.iRecursiveLock lock];
NSLog(@"%d",value);
recursiveMethod(value - 1);
[self.iRecursiveLock unlock];
}
};
recursiveMethod(10);
});
}
}
此時(shí)代碼會(huì)因?yàn)樽泳€程相互等待資源而造成線程死鎖。
@synchronized
@synchronized不管你幾條線程,不管你是否遞歸調(diào)用,它都支持,是我們最常用的一把鎖,雖然都在詬病其性能問題,可是在真機(jī)條件下測試其性能,和其他鎖并沒有那么明顯的差別。
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic ,assign) int count;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.count = 0;
[self synchronized_test]; // synchronized案例
}
- (void)synchronized_test {
for (int i=0; i<5; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^recursiveMethod)(int);
recursiveMethod = ^(int value){
if (value > 0) {
@synchronized(self) {
NSLog(@"%d",value);
recursiveMethod(value - 1);
}
}
};
recursiveMethod(10);
});
}
}
@end
@synchronized(obj)指令使用的obj為該鎖的唯一標(biāo)識(shí),只有當(dāng)標(biāo)識(shí)相同時(shí),才為滿足互斥。, @synchronized還是個(gè)遞歸可重入鎖,如下代碼所示:
NSObject *obj = [[NSObject alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
@synchronized(obj){
NSLog(@"1開始");
@synchronized (obj) {
NSLog(@"2開始");
@synchronized (obj) {
NSLog(@"3");
}
NSLog(@"2完成");
}
NSLog(@"1結(jié)束");
}
});
@synchronized是個(gè)遞歸互斥鎖,同一個(gè)線程可以重復(fù)獲得這個(gè)鎖并進(jìn)入執(zhí)行執(zhí)行塊里面的代碼而不會(huì)導(dǎo)致死鎖。
@synchronized的優(yōu)點(diǎn):不需要在代碼中顯式的創(chuàng)建鎖對象,便可以實(shí)現(xiàn)鎖的機(jī)制;遞歸互斥,同一個(gè)線程可以重復(fù)進(jìn)入而不導(dǎo)致死鎖。
@synchronized的缺點(diǎn):效率低(在真機(jī)上不見得效率那么低)。@synchronized塊會(huì)隱式的添加一個(gè)異常處理例程來保護(hù)代碼,該處理例程會(huì)在異常拋出的時(shí)候自動(dòng)的釋放互斥鎖,這會(huì)增加額外的開銷。同時(shí)為了實(shí)現(xiàn)遞歸互斥可重入,底層使用的是遞歸鎖加上復(fù)雜的業(yè)務(wù)邏輯,也增加了不少的消耗。
@synchronized加鎖需要一個(gè)對象參數(shù),在選著對象參數(shù)的時(shí)候要特別注意不能讓對象參數(shù)為nil,否則加鎖無效。
Semaphore信號(hào)量
同樣的信號(hào)量也可以解決線程安全問題,相關(guān)內(nèi)容請查閱GCD篇章,主要是控制并發(fā)數(shù)量,來實(shí)現(xiàn)線程安全
#pragma mark -- dispatch_semaphore_t
- (void)dispatch_semaphore_t_test {
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務(wù)1");
dispatch_semaphore_signal(sem);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務(wù)2");
dispatch_semaphore_signal(sem);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務(wù)3");
});
}
pthread_mutex
純C的鎖,需要我們自己進(jìn)行對象的內(nèi)存管理,前面有些鎖就是對齊進(jìn)行的封裝.
#pragma mark -- pthread_mutex
- (void)pthread_mutex_test {
//非遞歸加鎖
pthread_mutex_t lock0;
pthread_mutex_init(&lock0, NULL);
pthread_mutex_lock(&lock0);
// 鎖住的資源...
pthread_mutex_unlock(&lock0);
pthread_mutex_destroy(&lock0); // c對象,需要自己釋放資源
//遞歸加鎖
pthread_mutex_t lock;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); // 設(shè)置遞歸屬性
pthread_mutex_init(&lock, &attr);
pthread_mutexattr_destroy(&attr);
pthread_mutex_lock(&lock);
// 鎖住的資源...
pthread_mutex_unlock(&lock);
pthread_mutex_destroy(&lock); // c對象,需要自己釋放資源
}
讀寫鎖
讀寫鎖實(shí)際是一種特殊的自旋鎖,它把對共享資源的訪問者劃分成讀者和寫者,讀者只對共享資源進(jìn)行讀訪問,寫者則需要對共享資源進(jìn)行寫操作。這種鎖相對于自旋鎖而言,能提高并發(fā)性,因?yàn)樵?a rel="external nofollow" target="_blank">多處理器系統(tǒng)中,它允許同時(shí)有多個(gè)讀者來訪問共享資源,最大可能的讀者數(shù)為實(shí)際的邏輯CPU數(shù)。寫者是排他性的,一個(gè)讀寫鎖同時(shí)只能有一個(gè)寫者或多個(gè)讀者(與CPU數(shù)相關(guān)),但不能同時(shí)既有讀者又有寫者。在讀寫鎖保持期間也是搶占失效的。
如果讀寫鎖當(dāng)前沒有讀者,也沒有寫者,那么寫者可以立刻獲得讀寫鎖,否則它必須自旋在那里,直到?jīng)]有任何寫者或讀者。如果讀寫鎖沒有寫者,那么讀者可以立即獲得該讀寫鎖,否則讀者必須自旋在那里,直到寫者釋放該讀寫鎖。
讀寫鎖可以實(shí)現(xiàn)多讀單寫功能(讀讀并發(fā)、讀寫互斥、寫寫互斥) 我們通過GCD的柵欄函數(shù)實(shí)現(xiàn)的一個(gè)簡單讀寫鎖案例:
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic ,strong) dispatch_queue_t iQueue;
@property (nonatomic ,strong) NSMutableDictionary *dataDic;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.iQueue = dispatch_queue_create("AnAn", DISPATCH_QUEUE_CONCURRENT);
self.dataDic = [NSMutableDictionary new];
[self my_write: @"我是寫的東西"];
}
- (void)test {
for (int i = 0; i < 10; i ++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self my_read];
});
}
}
#pragma mark -- 讀寫鎖
- (NSString *)my_read {
// 異步讀取
__block NSString *ret;
dispatch_sync(self.iQueue, ^{
// 讀取的代碼
ret = self.dataDic[@"name"];
});
NSLog(@"%@",ret);
return ret;
}
- (void)my_write: (NSString *)name {
// 寫操作
dispatch_barrier_async(self.iQueue, ^{
[self.dataDic setObject:name forKey:@"name"];
});
}以上就是iOS開發(fā)常用線程安全鎖的詳細(xì)內(nèi)容,更多關(guān)于iOS線程安全鎖的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
iOS實(shí)現(xiàn)循環(huán)滾動(dòng)公告欄
這篇文章主要為大家詳細(xì)介紹了iOS實(shí)現(xiàn)循環(huán)滾動(dòng)公告欄,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-03-03
iOS基于UIScrollView實(shí)現(xiàn)滑動(dòng)引導(dǎo)頁
這篇文章主要為大家詳細(xì)介紹了iOS基于UIScrollView實(shí)現(xiàn)滑動(dòng)引導(dǎo)頁的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01
iOS開發(fā)tips-UINavigationBar的切換效果
這篇文章主要為大家詳細(xì)介紹了iOS開發(fā)tips-UINavigationBar的切換效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11
iOS開發(fā)TableView網(wǎng)絡(luò)請求及展示預(yù)加載實(shí)現(xiàn)示例
這篇文章主要為大家介紹了iOS開發(fā)TableView網(wǎng)絡(luò)請求及展示預(yù)加載實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07

