首先了解一些基本概念:git
臨界區:指的是一塊對公共資源進行訪問的代碼,並不是一種機制或是算法。github
自旋鎖:是用於多線程同步的一種鎖,線程反覆檢查鎖變量是否可用。因爲線程在這一過程當中保持執行,所以是一種忙等待。一旦獲取了自旋鎖,線程會一直保持該鎖,直至顯式釋放自旋鎖。 自旋鎖避免了進程上下文的調度開銷,所以對於線程只會阻塞很短期的場合是有效的。算法
互斥鎖(Mutex):是一種用於多線程編程中,防止兩條線程同時對同一公共資源(好比全局變量)進行讀寫的機制。該目的經過將代碼切片成一個一個的臨界區而達成。編程
讀寫鎖:是計算機程序的併發控制的一種同步機制,也稱「共享-互斥鎖」、多讀者-單寫者鎖) 用於解決多線程對公共資源讀寫問題。讀操做可併發重入,寫操做是互斥的。 讀寫鎖一般用互斥鎖、條件變量、信號量實現。安全
信號量(semaphore):是一種更高級的同步機制,互斥鎖能夠說是semaphore在僅取值0/1時的特例。信號量能夠有更多的取值空間,用來實現更加複雜的同步,而不僅僅是線程間互斥。bash
條件鎖:就是條件變量,當進程的某些資源要求不知足時就進入休眠,也就是鎖住了。當資源被分配到了,條件鎖打開,進程繼續運行。多線程
死鎖:指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去,這些永遠在互相等待的進程稱爲死鎖進程。併發
輪詢(Polling):一種CPU決策如何提供周邊設備服務的方式,又稱「程控輸出入」。輪詢法的概念是,由CPU定時發出詢問,依序詢問每個周邊設備是否須要其服務,有即給予服務,服務結束後再問下一個周邊,接着不斷周而復始。app
鎖的類型:異步
互斥鎖
自旋鎖
讀寫鎖
遞歸鎖
NSRecursiveLock
pthread_mutex(recursive)(見上)
條件鎖
信號量
//10000000
OSSpinLock: 112.38 ms
dispatch_semaphore: 160.37 ms
os_unfair_lock: 208.87 ms
pthread_mutex: 302.07 ms
NSCondition: 320.11 ms
NSLock: 331.80 ms
pthread_rwlock: 360.81 ms
pthread_mutex(recursive): 512.17 ms
NSRecursiveLock: 667.55 ms
NSConditionLock: 999.91 ms
@synchronized: 1654.92 ms
//1000
OSSpinLock: 0.02 ms
dispatch_semaphore: 0.03 ms
os_unfair_lock: 0.04 ms
pthread_mutex: 0.06 ms
NSLock: 0.06 ms
pthread_rwlock: 0.07 ms
NSCondition: 0.07 ms
pthread_mutex(recursive): 0.09 ms
NSRecursiveLock: 0.12 ms
NSConditionLock: 0.18 ms
@synchronized: 0.33 ms
複製代碼
atomic使用的是自旋鎖,主要用於賦值操做等輕量操做(散列表,引用計數,弱引用指針賦值),而互斥鎖通常都是鎖線程,好比單例。
首先了解一些基本概念:
因此:
在非主線程中更新UI就會有多個線程同時操做一個控件的可能,形成最後更新的結果不符合預期
多線程自己就是爲了併發處理以達到高效的目的,可是刷新UI使用併發會形成安全問題,要解決上面的安全問題,那就須要給控件加鎖,可是加鎖必然會形成額外的開銷,同時開新的線程自己就有必定的開銷,因此不如直接在主線程中執行更新操做。
@interface CustomDictionary ()
//多線程須要訪問的數據量
@property (nonatomic, strong) NSMutableDictionary *dataDic;
@end
//模擬場景,容許多個線程同時訪問字典,可是隻有一個線程能夠寫字典
@implementation CustomDictionary {
//定義一個併發隊列
dispatch_queue_t _concurrent_queue;
}
- (instancetype)init {
if (self = [super init]) {
_concurrent_queue = dispatch_queue_create("com.mf.read_write_queue", DISPATCH_QUEUE_CONCURRENT);
_dataDic = @{}.mutableCopy;
}
return self;
}
// 讀取數據,併發操做
- (id)objectForKey:(NSString *)key {
__block id obj;
//同步讀取數據
dispatch_sync(_concurrent_queue, ^{
obj = [self.dataDic objectForKey:key];
});
return obj;
}
// 寫入數據,異步柵欄
- (void)setObject:(id)obj forKey:(NSString *)key {
//異步柵欄調用設置數據
dispatch_barrier_async(_concurrent_queue, ^{
[self.dataDic setObject:obj forKey:key];
});
}
@end
複製代碼
dispatch_queue_t queue = dispatch_queue_create("com.mf.barrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"等待任務1,2上面執行完畢");
});
dispatch_async(queue, ^{
NSLog(@"3");
});
dispatch_async(queue, ^{
NSLog(@"4");
});
複製代碼
// 全局變量group
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 進入組(進入組和離開組必須成對出現, 不然會形成死鎖)
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"1");
//離開組
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
NSLog(@"2");
dispatch_group_leave(group);
});
dispatch_group_notify(group, queue, ^{ // 監聽組裏全部線程完成的狀況
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"任務1,2已完成");
});
});
複製代碼
//建立隊列
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
//建立操做
NSBlockOperation *operation1=[NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"執行第1次操做,線程:%@",[NSThread currentThread]);
}];
NSBlockOperation *operation2=[NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"執行第2次操做,線程:%@",[NSThread currentThread]);
}];
NSBlockOperation *operation3=[NSBlockOperation blockOperationWithBlock:^(){
NSLog(@"執行第3次操做,線程:%@",[NSThread currentThread]);
}];
//添加依賴
[operation1 addDependency:operation2];
[operation2 addDependency:operation3];
//將操做添加到隊列中去
[queue addOperation:operation1];
[queue addOperation:operation2];
[queue addOperation:operation3];
複製代碼
/* 好比說咱們須要請求三張元素圖,拼合成一張海報。咱們須要先對元素圖進行請求然後才能合成海報,這就造成了依賴關係。咱們經過semaphore限制資源數爲3,供請求元素圖使用,待請求完成後,釋放信號量,便能走到合成的耗時操做。 */
//建立信號量,參數:信號量的初值,若是小於0則會返回NULL
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
//元素圖1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//等待下降信號量
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"請求第1張元素圖");
sleep(1);
NSLog(@"第1張元素圖Get");
//提升信號量
dispatch_semaphore_signal(semaphore);
});
//元素圖2
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"請求第2張元素圖");
sleep(1);
NSLog(@"第2張元素圖Get");
dispatch_semaphore_signal(semaphore);
});
//元素圖3
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"請求第3張元素圖");
sleep(1);
NSLog(@"第3張元素圖Get");
dispatch_semaphore_signal(semaphore);
});
//合成海報
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"合成海報");
sleep(1);
NSLog(@"海報Get");
dispatch_semaphore_signal(semaphore);
});
複製代碼
注意:
正常的使用順序是先下降而後再提升,這兩個函數一般成對使用。
郵箱: adrenine@163.com
郵箱: holaux@gmail.com
郵箱: ledahapple@icloud.com