iOS 加鎖探究

1. iOS中的互斥鎖

在編程中,引入對象互斥鎖的概念,來保證共享數(shù)據(jù)操作的完整性。每個對象都對應(yīng)于一個可稱為“互斥鎖”的標記,這個標記用來保證在任一時刻,只能有一個線程訪問對象。

1.1 @synchronized (self)

-?(void)lock1?{

@synchronized(self)?{

//?加鎖操作

}

}

1.2 NSLock

-?(void)lock2?{

NSLock*xwlock?=?[[NSLockalloc]?init];

XWLogBlock?logBlock?=?^?(NSArray*array)?{

[xwlock?lock];

for(idobjinarray)?{

NSLog(@"%@",obj);

}

[xwlock?unlock];

};

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),?^{

NSArray*array?=?@[@1,@2,@3];

logBlock(array);

});

}

1.3 pthread

pthread除了創(chuàng)建互斥鎖,還可以創(chuàng)建遞歸鎖、讀寫鎖、once等鎖

__block?pthread_mutex_t?mutex;

pthread_mutex_init(&mutex,NULL);

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),?^{

NSLog(@"+++++?線程1?start");

pthread_mutex_lock(&mutex);

sleep(2);

pthread_mutex_unlock(&mutex);

NSLog(@"+++++?線程1?end");

});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),?^{

NSLog(@"-----?線程2?start");

pthread_mutex_lock(&mutex);

sleep(3);

pthread_mutex_unlock(&mutex);

NSLog(@"-----?線程2?end");

});

}

2. iOS中的遞歸鎖

同一個線程可以多次加鎖,不會造成死鎖

死鎖->

-?(void)lock5?{

NSLock?*commonLock?=?[[NSLock?alloc]?init];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),?^{

staticvoid(^XWRecursiveBlock)(int);

XWRecursiveBlock?=?^(intvalue)?{

[commonLock?lock];

if(value>0)?{

NSLog(@"加鎖層數(shù):?%d",value);

sleep(1);

XWRecursiveBlock(--value);

}

NSLog(@"程序退出!");

[commonLock?unlock];

};

XWRecursiveBlock(3);

});

}

<-死鎖

2.1 NSRecursiveLock

-?(void)lock4?{

NSRecursiveLock?*recursiveLock?=?[[NSRecursiveLock?alloc]?init];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),?^{

staticvoid(^XWRecursiveBlock)(int);

XWRecursiveBlock?=?^(intvalue)?{

[recursiveLock?lock];

if(value>0)?{

NSLog(@"加鎖層數(shù):?%d",value);

sleep(1);

XWRecursiveBlock(--value);

}

NSLog(@"程序退出!");

[recursiveLock?unlock];

};

XWRecursiveBlock(3);

});

}

2.2 pthread

-?(void)lock6?{

__block?pthread_mutex_t?recursiveMutex;

pthread_mutexattr_t?recursiveMutexattr;

pthread_mutexattr_init(&recursiveMutexattr);

pthread_mutexattr_settype(&recursiveMutexattr,?PTHREAD_MUTEX_RECURSIVE);

pthread_mutex_init(&recursiveMutex,?&recursiveMutexattr);

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),?^{

staticvoid(^XWRecursiveBlock)(int);

XWRecursiveBlock?=?^(intvalue)?{

pthread_mutex_lock(&recursiveMutex);

if(value>0)?{

NSLog(@"加鎖層數(shù):?%d",value);

sleep(1);

XWRecursiveBlock(--value);

}

NSLog(@"程序退出!");

pthread_mutex_unlock(&recursiveMutex);

};

XWRecursiveBlock(3);

});

}

3. 信號量

信號量(Semaphore),有時被稱為信號燈,是在多線程環(huán)境下使用的一種設(shè)施,是可以用來保證兩個或多個關(guān)鍵代碼段不被并發(fā)調(diào)用。在進入一個關(guān)鍵代碼段之前,線程必須獲取一個信號量;一旦該關(guān)鍵代碼段完成了,那么該線程必須釋放信號量。其它想進入該關(guān)鍵代碼段的線程必須等待直到第一個線程釋放信號量

3.1 dispatch_semaphore_t

實現(xiàn) GCD 下同步

-?(void)lock7?{

//????dispatch_semaphore_create?//創(chuàng)建一個信號量?semaphore

//????dispatch_semaphore_signal?//發(fā)送一個信號?信號量+1

//????dispatch_semaphore_wait???//?等待信號?信號量-1

///?需求:?異步線程的兩個操作同步執(zhí)行

dispatch_semaphore_t?semaphone?=?dispatch_semaphore_create(0);

NSLog(@"start");

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),?^{

NSLog(@"async?....?");

sleep(5);

///?線程資源?+?1

dispatch_semaphore_signal(semaphone);//信號量+1

});

///?當前線程資源數(shù)量為?0?,等待激活

dispatch_semaphore_wait(semaphone,?DISPATCH_TIME_FOREVER);

NSLog(@"end");

}

3.2 pthread

-?(void)lock8?{

__block?pthread_mutex_t?semaphore?=?PTHREAD_MUTEX_INITIALIZER;

__block?pthread_cond_t?cond?=?PTHREAD_COND_INITIALIZER;

NSLog(@"start");

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),?^{

pthread_mutex_lock(&semaphore);

NSLog(@"async...");

sleep(5);

pthread_cond_signal(&cond);

pthread_mutex_unlock(&semaphore);

});

pthread_cond_wait(&cond,?&semaphore);

NSLog(@"end");

}

4. 條件鎖

4.1 NSCondition

NSCondition 的對象實際上是作為一個鎖和線程檢查器,鎖主要是為了檢測條件時保護數(shù)據(jù)源,執(zhí)行條件引發(fā)的任務(wù)。線程檢查器主要是根據(jù)條件決定是否繼續(xù)運行線程,即線程是否被阻塞。

NSCondition同樣實現(xiàn)了NSLocking協(xié)議,所以它和NSLock一樣,也有NSLocking協(xié)議的lock和unlock方法,可以當做NSLock來使用解決線程同步問題,用法完全一樣。

-?(NSMutableArray*)removeLastImage:(NSMutableArray*)images?{

if(images.count?>0)?{

NSCondition*condition?=?[[NSConditionalloc]?init];

[condition?lock];

[images?removeLastObject];

[condition?unlock];

NSLog(@"removeLastImage?%@",images);

returnimages;

}else{

returnNULL;

}

}

同時,NSCondition提供更高級的用法。wait和signal,和條件信號量類似。

NSCondition和NSLock、@synchronized等是不同的是,NSCondition可以給每個線程分別加鎖,加鎖后不影響其他線程進入臨界區(qū)。這是非常強大。

-?(void)lock10?{

self.conditionArray?=?[NSMutableArrayarray];

self.xwCondition?=?[[NSConditionalloc]?init];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),?^{

[self.xwCondition?lock];

if(self.conditionArray.count?==0)?{

NSLog(@"等待制作數(shù)組");

[self.xwCondition?wait];

}

idobj?=self.conditionArray[0];

NSLog(@"獲取對象進行操作:%@",obj);

[self.xwCondition?unlock];

});

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),?^{

[self.xwCondition?lock];

idobj?=@"極客";

[self.conditionArray?addObject:obj];

NSLog(@"創(chuàng)建了一個對象:%@",obj);

[self.xwCondition?signal];

[self.xwCondition?unlock];

});

}

4.2 NSConditionLock

-?(void)lock11?{

NSConditionLock*conditionLock?=?[[NSConditionLockalloc]?init];

NSMutableArray*arrayM?=?[NSMutableArrayarray];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),?^{

[conditionLock?lock];

for(inti?=0;?i?<6;?i++)?{

[arrayM?addObject:@(i)];

NSLog(@"異步下載第?%d?張圖片",i);

sleep(1);

if(arrayM.count?==4)?{

[conditionLock?unlockWithCondition:4];

}

}

});

dispatch_async(dispatch_get_main_queue(),?^{

[conditionLock?lockWhenCondition:4];

NSLog(@"已經(jīng)獲取到4張圖片->主線程渲染");

[conditionLock?unlock];

});

}

4.3 pthread POSIX Conditions

-?(void)lock12?{

__block?pthread_mutex_t?mutex;

__block?pthread_cond_t?condition;

pthread_mutex_init(&mutex,NULL);

pthread_cond_init(&condition,NULL);

NSMutableArray*arrayM?=?[NSMutableArrayarray];

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),?^{

pthread_mutex_lock(&mutex);

for(inti?=0;?i?<6;?i++)?{

[arrayM?addObject:@(i)];

NSLog(@"異步下載第?%d?張圖片",i);

sleep(1);

if(arrayM.count?==4)?{

pthread_cond_signal(&condition);

}

}

});

dispatch_async(dispatch_get_main_queue(),?^{

pthread_cond_wait(&condition,?&mutex);

NSLog(@"已經(jīng)獲取到4張圖片->主線程渲染");

pthread_mutex_unlock(&mutex);

});

}

5. 讀寫鎖

讀寫鎖實際是一種特殊的自旋鎖,它把對共享資源的訪問者劃分成讀者和寫者,讀者只對共享資源進行讀訪問,寫者則需要對共享資源進行寫操作。這種鎖相對于自旋鎖而言,能提高并發(fā)性,因為在多處理器系統(tǒng)中,它允許同時有多個讀者來訪問共享資源,最大可能的讀者數(shù)為實際的邏輯CPU數(shù)。寫者是排他性的,一個讀寫鎖同時只能有一個寫者或多個讀者(與CPU數(shù)相關(guān)),但不能同時既有讀者又有寫者。

5.1 dispatch_barrier_async / dispatch_barrier_sync

有一個需求,如圖:


任務(wù)1,2,3 均執(zhí)行完畢執(zhí)行任務(wù)0,然后執(zhí)行任務(wù)4,5,6.

-?(void)lock13?{

dispatch_queue_tqueue?=?dispatch_queue_create("com.qiuxuewei.brrier",?DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue,?^{

NSLog(@"任務(wù)1?--?%@",[NSThreadcurrentThread]);

});

dispatch_async(queue,?^{

NSLog(@"任務(wù)2?--?%@",[NSThreadcurrentThread]);

});

dispatch_async(queue,?^{

NSLog(@"任務(wù)3?--?%@",[NSThreadcurrentThread]);

});

dispatch_barrier_sync(queue,?^{

NSLog(@"任務(wù)0?--?%@",[NSThreadcurrentThread]);

for(inti?=0;?i?<100;?i++)?{

if(i?%30==0)?{

NSLog(@"任務(wù)0?---?log:%d?--?%@",i,[NSThreadcurrentThread]);

}

}

});

NSLog(@"dispatch_barrier_sync??down!!!");

dispatch_async(queue,?^{

NSLog(@"任務(wù)4?--?%@",[NSThreadcurrentThread]);

});

dispatch_async(queue,?^{

NSLog(@"任務(wù)5?--?%@",[NSThreadcurrentThread]);

});

dispatch_async(queue,?^{

NSLog(@"任務(wù)6?--?%@",[NSThreadcurrentThread]);

});

}

dispatch_barrier_async 和 dispatch_barrier_sync 的異同

共同點

等待它前面的執(zhí)行完才執(zhí)行自己的任務(wù)

等待自己任務(wù)執(zhí)行結(jié)束才執(zhí)行后面的任務(wù)

不同點

dispatch_barrier_async 將自己的任務(wù)插入到隊列之后會繼續(xù)將后面的操作插入到隊列,按照規(guī)則先執(zhí)行前面隊列的任務(wù),等自己隊列執(zhí)行完畢,再執(zhí)行后面隊列的任務(wù)

dispatch_barrier_sync ?將自己的任務(wù)插入到隊列之后,先等待自己的任務(wù)執(zhí)行完畢才會執(zhí)行后面操作插入到隊列,再執(zhí)行后面隊列的任務(wù)。

5.2 pthread

-?(void)lock14?{

__block?pthread_rwlock_t?rwlock;

pthread_rwlock_init(&rwlock,NULL);

__blockNSMutableArray*arrayM?=?[NSMutableArrayarray];

XWBlock?writeBlock?=?^?(NSString*str)?{

NSLog(@"開啟寫操作");

pthread_rwlock_wrlock(&rwlock);

[arrayM?addObject:str];

sleep(2);

pthread_rwlock_unlock(&rwlock);

};

XWVoidBlock?readBlock?=?^?{

NSLog(@"開啟讀操作");

pthread_rwlock_rdlock(&rwlock);

sleep(1);

NSLog(@"讀取數(shù)據(jù):%@",arrayM);

pthread_rwlock_unlock(&rwlock);

};

for(inti?=0;?i?<5;?i++)?{

dispatch_async(dispatch_get_global_queue(0,0),?^{

writeBlock([NSStringstringWithFormat:@"%d",i]);

});

}

for(inti?=0;?i?<10;?i++)?{

dispatch_async(dispatch_get_global_queue(0,0),?^{

readBlock();

});

}

}

6.自旋鎖

boollock=false;//?一開始沒有鎖上,任何線程都可以申請鎖??

do{

while(lock);//?如果?lock?為?true?就一直死循環(huán),相當于申請鎖

lock=true;//?掛上鎖,這樣別的線程就無法獲得鎖

Critical?section//?臨界區(qū)

lock=false;//?相當于釋放鎖,這樣別的線程可以進入臨界區(qū)

Reminder?section//?不需要鎖保護的代碼????????

}

6.1 OSSpinLock

YYKit作者的文章?不再安全的 OSSpinLock有說到這個自旋鎖存在優(yōu)先級反轉(zhuǎn)的問題。

6.2 os_unfair_lock

自旋鎖已經(jīng)不再安全,然后蘋果又整出來個 os_unfair_lock_t ,這個鎖解決了優(yōu)先級反轉(zhuǎn)的問題。

os_unfair_lock_tunfairLock;

unfairLock?=?&(OS_UNFAIR_LOCK_INIT);

os_unfair_lock_lock(unfairLock);

os_unfair_lock_unlock(unfairLock);

7. property - atomic / nonatomic

atomic 修飾的對象,系統(tǒng)會保證在其自動生成的 getter/setter 方法中的操作是完整的,不受其他線程的影響。例如 A 線程在執(zhí)行 getter 方法時,B線程執(zhí)行了 setter 方法,此時 A 線程依然會得到一個完整無損的對象。

atomic

默認修飾符

會保證CPU能在別的線程訪問這個屬性之前先執(zhí)行完當前操作

讀寫速度慢

線程不安全 - 如果有另一個線程 D 同時在調(diào)[name release],那可能就會crash,因為 release 不受 getter/setter 操作的限制。也就是說,這個屬性只能說是讀/寫安全的,但并不是線程安全的,因為別的線程還能進行讀寫之外的其他操作。線程安全需要開發(fā)者自己來保證。

nonatomic

不默認

速度更快

線程不安全

如果兩個線程同時訪問會出現(xiàn)不可預(yù)料的結(jié)果。

8. Once 原子操作

8.1 GCD

-?(id)lock15?{

staticidshareInstance;

staticdispatch_once_tonceToken;

dispatch_once(&onceToken,?^{

if(!shareInstance)?{

shareInstance?=?[[NSObjectalloc]?init];

}

});

returnshareInstance;

}

8.2 pthread

-?(void)lock16?{

pthread_once_tonce?=?PTHREAD_ONCE_INIT;

pthread_once(&once,?lock16Func);

}

voidlock16Func(){

staticid?shareInstance;

shareInstance?=?[[NSObject?alloc]?init];

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容