前言:本文主要是關(guān)于GCD和NSOperation的具體使用,以及關(guān)于線程安全的實現(xiàn).
線程以及進(jìn)程的基本概念就不贅述了.
GCD
首先需要明確幾個基本的概念:
- 串行(同步)執(zhí)行與并發(fā)(異步)執(zhí)行
- 同步函數(shù)與異步函數(shù)
- 串行隊列與并發(fā)隊列
串行執(zhí)行與并發(fā)執(zhí)行
這里的串行執(zhí)行與并發(fā)執(zhí)行決定的是任務(wù)的執(zhí)行方式
- 串行執(zhí)行: 如果我沒有執(zhí)行完,那么后面的將永遠(yuǎn)無法執(zhí)行
- 并發(fā)執(zhí)行:我無所謂,我沒執(zhí)行完后面的也可以執(zhí)行
同步函數(shù)與異步函數(shù)
- 同步函數(shù)(disaptach_sync):
特點:只能在當(dāng)前線程中執(zhí)行任務(wù)盗舰,不具備開啟新線程的能力
執(zhí)行方式:同步 - 異步函數(shù)(disaptach_async):
特點:可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
執(zhí)行方式:異步
注意:任務(wù)從隊列中取出來時時有有序的,遵循FIFO原則,先進(jìn)先出,但是任務(wù)在線程中執(zhí)行是無序的,并且如果遇到卡頓 阻塞時 可以先繼續(xù)往下執(zhí)行,等前面的任務(wù)執(zhí)行完畢之后再回頭執(zhí)行之前遇到卡頓沒有執(zhí)行的任務(wù).
串行隊列與并發(fā)隊列
說到隊列就得提下GCD中的兩個核心概念: 任務(wù)與隊列
任務(wù): 執(zhí)行什么操作 隊列: 用來存放任務(wù)
隊列分兩種就是我們要說的串行隊列與并發(fā)隊列
- Serial Diapatch Queue 串行隊列
當(dāng)任務(wù)相互依賴桂躏,具有明顯的先后順序的時候钻趋,使用串行隊列是一個不錯的選擇 創(chuàng)建一個串行隊列:
dispatch_queue_t serialDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_SERIAL);
第一個參數(shù)為隊列名,第二個參數(shù)為隊列類型剂习,當(dāng)然蛮位,第二個參數(shù)如果寫NULL,創(chuàng)建出來的也是一個串行隊列鳞绕。
通過異步函數(shù)來執(zhí)行
dispatch_async(serialDispatchQueue, ^{
NSLog(@"1---%@",[NSThread currentThread]);
});
dispatch_async(serialDispatchQueue, ^{
sleep(2);
NSLog(@"2---%@",[NSThread currentThread]);
});
dispatch_async(serialDispatchQueue, ^{
sleep(1);
NSLog(@"3----%@",[NSThread currentThread]);
});
控制臺log的結(jié)果
2017-05-24 15:57:02.842495 多線程[14286:6618708] 1---<NSThread: 0x170072ec0>{number = 3, name = (null)}
2017-05-24 15:57:04.847125 多線程[14286:6618708] 2---<NSThread: 0x170072ec0>{number = 3, name = (null)}
2017-05-24 15:57:05.852685 多線程[14286:6618708] 3----<NSThread: 0x170072ec0>{number = 3, name = (null)}
可以看到三個任務(wù)是在開啟的子線程中串行輸出失仁,相互彼此依賴,串行執(zhí)行
- Concurrent Diapatch Queue 并發(fā)隊列
與串行隊列剛好相反们何,他不會存在任務(wù)間的相互依賴萄焦。
創(chuàng)建一個并發(fā)隊列:
dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_CONCURRENT);
比較2個隊列的創(chuàng)建,我們發(fā)現(xiàn)只有第二個參數(shù)從DISPATCH_QUEUE_SERIAL變成了對應(yīng)的DISPATCH_QUEUE_CONCURRENT冤竹,其他完全一樣拂封。
用同一段代碼,換一種隊列我們來比較一下效果:
dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentDispatchQueue, ^{
NSLog(@"1---%@",[NSThread currentThread]);
});
dispatch_async(concurrentDispatchQueue, ^{
sleep(2);
NSLog(@"2---%@",[NSThread currentThread]);
});
dispatch_async(concurrentDispatchQueue, ^{
sleep(1);
NSLog(@"3----%@",[NSThread currentThread]);
});
2017-05-24 17:10:46.003170 多線程[14299:6629156] 1---<NSThread: 0x170075300>{number = 3, name = (null)}
2017-05-24 17:10:47.005762 多線程[14299:6629157] 3----<NSThread: 0x174061cc0>{number = 4, name = (null)}
2017-05-24 17:10:48.007882 多線程[14299:6629156] 2---<NSThread: 0x170075300>{number = 3, name = (null)}
我們發(fā)現(xiàn)鹦蠕,log的輸出在3個不同編號的線程中進(jìn)行冒签,而且相互不依賴,不阻塞钟病。
- Global Queue & Main Queue
這是系統(tǒng)為我們準(zhǔn)備的2個隊列:
Global Queue 其實就是系統(tǒng)創(chuàng)建的Concurrent Diapatch Queue
Main Queue 其實就是系統(tǒng)創(chuàng)建的位于主線程的Serial Diapatch Queue
通常情況我們會把這2個隊列放在一起使用萧恕,也是我們最常用的開異步線程-執(zhí)行異步任務(wù)-回主線程的一種方式:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"異步線程");
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"異步主線程");
});
});
通過上面的代碼我們發(fā)現(xiàn)了2個點:
- dispatch_get_global_queue存在優(yōu)先級,沒錯肠阱,他一共有4個優(yōu)先級:
define DISPATCH_QUEUE_PRIORITY_HIGH 2
define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
define DISPATCH_QUEUE_PRIORITY_LOW (-2)
define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
NSLog(@"4");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
NSLog(@"3");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"2");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
NSLog(@"1");
});
在指定優(yōu)先級之后廊鸥,同一個隊列會按照這個優(yōu)先級執(zhí)行,打印的順序為1辖所、2、3磨德、4缘回,當(dāng)然這不是串行隊列,所以不存在絕對回調(diào)先后典挑。
異步主線程
在日常工作中酥宴,除了在其他線程返回主線程的時候需要用這個方法,還有一些時候我們在主線程中直接調(diào)用異步主線程您觉,這是利用dispatch_async的特性:block中的任務(wù)會放在主線程本次runloop之后返回拙寡。這樣,有些存在先后順序的問題就可以得到解決了琳水。
####關(guān)于線程與隊列,以及任務(wù)的執(zhí)行方式總結(jié)
![046EB302-B057-4D4D-9919-180BA087F1A0.png](http://upload-images.jianshu.io/upload_images/1921775-40c0d60acf555da7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
*這里需要注意: 同步函數(shù)和主隊列相結(jié)合時: 因為同步函數(shù)不會開新的線程,任務(wù)是在當(dāng)前的線程中執(zhí)行,如果當(dāng)前線程是主線程,那么會造成死鎖,也就是隊列里面的任務(wù)都執(zhí)行不了.造成的原因是: 因為主隊列中的任務(wù)是需要在主線程中執(zhí)行,而在執(zhí)行之前會先檢查主線程的狀態(tài),如果發(fā)現(xiàn)主線程正在執(zhí)行任務(wù),那么就會暫停隊列中任務(wù)的調(diào)度,這里需要明白使用同步函數(shù)封裝任務(wù)時,任務(wù)的執(zhí)行必須得拿到當(dāng)前函數(shù)的返回值,才會繼續(xù)往下面執(zhí)行, 因為每一條代碼都是一個任務(wù),當(dāng)主線程執(zhí)行到當(dāng)前同步函數(shù)時,也需要去執(zhí)行里面封裝的block任務(wù)塊,而此時任務(wù)塊正在等主線程執(zhí)行完任務(wù),所以就沒辦法被執(zhí)行,所以當(dāng)前的同步函數(shù)就執(zhí)行不完,也就拿不到返回值,所以就會執(zhí)行不下去,造成了死鎖.
相反回到最開始,如果當(dāng)前線程不是主線程,而是新開的子線程的話就不會造成死鎖,原因還是:主隊列本身的任務(wù)調(diào)度時的特點
所以切記: 主線程不能與主隊列結(jié)合使用.*
*異步函數(shù)與主隊列結(jié)合時不會造成死鎖,原因是: 首先明確當(dāng)前情況下不會開新的線程,都是在主線程中執(zhí)行的, 同樣主隊列中的任務(wù)執(zhí)行前也會判斷主線程的狀態(tài),當(dāng)前主線程正在執(zhí)行異步函數(shù)這個任務(wù),所以也會暫停調(diào)度主隊列中的任務(wù), 但是由于異步函數(shù)本身的特點,可以繼續(xù)往下執(zhí)行,先把每個異步函數(shù)代碼都執(zhí)行完之后,回頭再去執(zhí)行每個異步函數(shù)里面封裝的任務(wù).*
####GCD中其它常用的函數(shù)
- 主線程中延遲執(zhí)行:dispatch_after
這個是最常用的肆糕,用來延遲執(zhí)行的GCD方法般堆,因為在主線程中我們不能用sleep來延遲方法的調(diào)用,所以用它是最合適的.
用三種隊列進(jìn)行了測試
dispatch_queue_t serialDispatchQueue = dispatch_queue_create("com.test.queue", DISPATCH_QUEUE_SERIAL);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"主隊列----%@",[NSThread currentThread]);
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_global_queue(0, 0), ^{
NSLog(@"全局并發(fā)隊列----%@",[NSThread currentThread]);
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), serialDispatchQueue, ^{
NSLog(@"串行隊列----%@",[NSThread currentThread]);
});
控制臺輸出結(jié)果
2017-05-24 18:07:22.709788 09-掌握-GCD的常用函數(shù)[14307:6637921] 主隊列----<NSThread: 0x17007a6c0>{number = 1, name = main}
2017-05-24 18:07:22.710247 09-掌握-GCD的常用函數(shù)[14307:6638005] 全局并發(fā)隊列----<NSThread: 0x170267bc0>{number = 3, name = (null)}
2017-05-24 18:07:22.710652 09-掌握-GCD的常用函數(shù)[14307:6638071] 串行隊列----<NSThread: 0x17026ba40>{number = 4, name = (null)}
結(jié)論:
a.GCD延遲執(zhí)行的原理是,延遲把block內(nèi)容提交到隊列而非直接添加到隊列后延遲調(diào)度
b.GCD可以通過設(shè)置隊列來控制block在哪個線程中調(diào)用
c.dispatch_after方法本身是異步的(類似dispatch_async) 具備開啟線程的能力
- 柵欄函數(shù):dispatch_barrier_async
直接上代碼
//創(chuàng)建隊列
dispatch_queue_t queue = dispatch_queue_create("szl", DISPATCH_QUEUE_CONCURRENT);
//2添加任務(wù)
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 50; i++) {
NSLog(@"1-%zd---%@",i,[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 10; i++) {
NSLog(@"2-%zd---%@",i,[NSThread currentThread]);
}
});
//控制順序:必須等任務(wù)1和2執(zhí)行完畢之后才能執(zhí)行任務(wù)3和任務(wù)4
dispatch_barrier_async(queue, ^{
NSLog(@"+++++++++++++++++++++++++++");
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i <10; i++) {
NSLog(@"3-%zd---%@",i,[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i <10; i++) {
NSLog(@"4-%zd---%@",i,[NSThread currentThread]);
}
});
控制臺輸出結(jié)果
2017-05-25 12:03:40.173683 多線程[14462:6789088] 1-0---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.173840 多線程[14462:6789088] 1-1---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.173877 多線程[14462:6789088] 1-2---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.173910 多線程[14462:6789088] 1-3---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.173943 多線程[14462:6789088] 1-4---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.173976 多線程[14462:6789088] 1-5---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.174008 多線程[14462:6789088] 1-6---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.174041 多線程[14462:6789088] 1-7---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.174073 多線程[14462:6789088] 1-8---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.174106 多線程[14462:6789088] 1-9---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.174169 多線程[14462:6789089] 2-0---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.176032 多線程[14462:6789088] 1-10---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177201 多線程[14462:6789089] 2-1---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.177252 多線程[14462:6789088] 1-11---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177348 多線程[14462:6789088] 1-12---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177386 多線程[14462:6789088] 1-13---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177420 多線程[14462:6789088] 1-14---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177605 多線程[14462:6789088] 1-15---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177650 多線程[14462:6789088] 1-16---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177664 多線程[14462:6789089] 2-2---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.177807 多線程[14462:6789088] 1-17---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177855 多線程[14462:6789089] 2-3---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.177897 多線程[14462:6789088] 1-18---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.177939 多線程[14462:6789089] 2-4---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.177982 多線程[14462:6789088] 1-19---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.178287 多線程[14462:6789089] 2-5---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.178620 多線程[14462:6789088] 1-20---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.178678 多線程[14462:6789089] 2-6---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.178721 多線程[14462:6789088] 1-21---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.179618 多線程[14462:6789089] 2-7---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.180078 多線程[14462:6789088] 1-22---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.180354 多線程[14462:6789089] 2-8---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.180430 多線程[14462:6789088] 1-23---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.180476 多線程[14462:6789089] 2-9---<NSThread: 0x17406d700>{number = 4, name = (null)}
2017-05-25 12:03:40.180518 多線程[14462:6789088] 1-24---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.180586 多線程[14462:6789088] 1-25---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.180619 多線程[14462:6789088] 1-26---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.180652 多線程[14462:6789088] 1-27---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182044 多線程[14462:6789088] 1-28---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182085 多線程[14462:6789088] 1-29---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182119 多線程[14462:6789088] 1-30---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182153 多線程[14462:6789088] 1-31---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182185 多線程[14462:6789088] 1-32---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182218 多線程[14462:6789088] 1-33---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182251 多線程[14462:6789088] 1-34---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182284 多線程[14462:6789088] 1-35---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182317 多線程[14462:6789088] 1-36---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182440 多線程[14462:6789088] 1-37---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182474 多線程[14462:6789088] 1-38---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182506 多線程[14462:6789088] 1-39---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182539 多線程[14462:6789088] 1-40---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182577 多線程[14462:6789088] 1-41---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182618 多線程[14462:6789088] 1-42---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182651 多線程[14462:6789088] 1-43---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182683 多線程[14462:6789088] 1-44---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.182715 多線程[14462:6789088] 1-45---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.190503 多線程[14462:6789088] 1-46---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.190563 多線程[14462:6789088] 1-47---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.190598 多線程[14462:6789088] 1-48---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.190806 多線程[14462:6789088] 1-49---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.190895 多線程[14462:6789090] +++++++++++++++++++++++++++
2017-05-25 12:03:40.191051 多線程[14462:6789090] 3-0---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.191551 多線程[14462:6789090] 3-1---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.191607 多線程[14462:6789090] 3-2---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.191640 多線程[14462:6789090] 3-3---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.191672 多線程[14462:6789090] 3-4---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.191715 多線程[14462:6789088] 4-0---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.192233 多線程[14462:6789090] 3-5---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.192313 多線程[14462:6789090] 3-6---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.192347 多線程[14462:6789090] 3-7---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.192379 多線程[14462:6789090] 3-8---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.192418 多線程[14462:6789090] 3-9---<NSThread: 0x170077e40>{number = 5, name = (null)}
2017-05-25 12:03:40.192491 多線程[14462:6789088] 4-1---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.192525 多線程[14462:6789088] 4-2---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.192557 多線程[14462:6789088] 4-3---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.192704 多線程[14462:6789088] 4-4---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.192752 多線程[14462:6789088] 4-5---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.192920 多線程[14462:6789088] 4-6---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.193071 多線程[14462:6789088] 4-7---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.193108 多線程[14462:6789088] 4-8---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
2017-05-25 12:03:40.193698 多線程[14462:6789088] 4-9---<NSThread: 0x17407a6c0>{number = 3, name = (null)}
通過打印信息可以看到任務(wù)3和任務(wù)4是在任務(wù)1和任務(wù)2完全執(zhí)行完之后才執(zhí)行的
我們經(jīng)常使用圖片下載框架SDWebImage里面就用到了這個柵欄函數(shù)來控制任務(wù)執(zhí)行的順序 具體代碼可以在SDWebImage中搜索dispatch_barrier_async
- dispatch_group
當(dāng)我們需要監(jiān)聽一個并發(fā)隊列中诚啃,所有任務(wù)都完成了淮摔,就可以用到這個group,因為并發(fā)隊列你并不知道哪一個是最后執(zhí)行的始赎,所以以單獨一個任務(wù)是無法監(jiān)聽到這個點的和橙,如果把這些單任務(wù)都放到同一個group,那么造垛,我們就能通過dispatch_group_notify方法知道什么時候這些任務(wù)全部執(zhí)行完成了魔招。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{NSLog(@"0");});
dispatch_group_async(group, queue, ^{NSLog(@"1");});
dispatch_group_async(group, queue, ^{NSLog(@"2");});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"down");});
在例子中,我把3個log分別放在并發(fā)隊列中五辽,通過把這個并發(fā)隊列任務(wù)統(tǒng)一加入group中办斑,group每次runloop的時候都會調(diào)用一個方法dispatch_group_wait(group, DISPATCH_TIME_NOW),用來檢查group中的任務(wù)是否已經(jīng)完成奔脐,如果已經(jīng)完成了俄周,那么會執(zhí)行dispatch_group_notify的block,輸出’down’看一下運行結(jié)果:
2017-05-25 14:32:48.965429 多線程[14500:6812849] 0
2017-05-25 14:32:48.965513 多線程[14500:6812849] 1
2017-05-25 14:32:48.965533 多線程[14500:6812849] 2
2017-05-25 14:32:49.067608 多線程[14500:6812807] down
- dispatch_apply 快速迭代
這個方法用于無序查找髓迎,在一個數(shù)組中峦朗,我們能開啟多個線程來查找所需要的值
NSArray *array=[[NSArray alloc ] initWithObjects:@"0",@"1",@"2",@"3",@"4",@"5",@"6", nil];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);//創(chuàng)建隊列
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%zd---%@----%@",index, [array objectAtIndex:index], [NSThread currentThread]);
});
輸出結(jié)果
2017-05-25 14:40:23.350677 多線程[14505:6815659] 0---0---<NSThread: 0x174064bc0>{number = 1, name = main}
2017-05-25 14:40:23.350668 多線程[14505:6815678] 1---1---<NSThread: 0x1700700c0>{number = 3, name = (null)}
2017-05-25 14:40:23.350821 多線程[14505:6815659] 2---2---<NSThread: 0x174064bc0>{number = 1, name = main}
2017-05-25 14:40:23.350962 多線程[14505:6815659] 4---4---<NSThread: 0x174064bc0>{number = 1, name = main}
2017-05-25 14:40:23.351006 多線程[14505:6815659] 5---5---<NSThread: 0x174064bc0>{number = 1, name = main}
2017-05-25 14:40:23.351049 多線程[14505:6815659] 6---6---<NSThread: 0x174064bc0>{number = 1, name = main}
2017-05-25 14:40:23.351084 多線程[14505:6815678] 3---3---<NSThread: 0x1700700c0>{number = 3, name = (null)}
可以看到參與迭代遍歷的有子線程也有主線程,這里就要注意一個點:主線程也會參與迭代的過程,里面的任務(wù)是并發(fā)執(zhí)行的,所以迭代時不能傳主隊列.
- dispatch_once
這個經(jīng)常使用,創(chuàng)建單例的時候
整個應(yīng)用程序中只會執(zhí)行一次
本身是線程安全的,所以不用去擔(dān)心線程安全的問題
onceToken 是用來記錄該部分代碼是否被執(zhí)行過
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ //第一次執(zhí)行代碼時 onecToken值為0,之后就都是 -1
NSLog(@"--once---%@",[NSThread currentThread]);
});
#####線程安全與線程間通訊
- 線程安全
多線程安全隱患:
資源共享,多個線程同時訪問同一塊資源,比如同時訪問同一個對象,同一個變量,同一個文件, 這個時候就容易引發(fā)多線程安全的問題,容易引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全的問題.
解決方案: 加鎖
加互斥鎖
把相關(guān)代碼 加在 @synchronized(objc){} 中,()中的objc稱為鎖對象,只要是唯一的就可以.當(dāng)每個線程來執(zhí)行加鎖里面的代碼時會先加把鎖,執(zhí)行完之后再解鎖.這樣下條線程會等待之前線程執(zhí)行完之后,再去執(zhí)行.
舉例
@synchronized(self) {
//1.檢查余票
NSInteger count = self.totalCount;
if (count >0) {
for (NSInteger i = 0; i<100000; i++) {
//演示耗時操作
}
//2.賣出去一張票
self.totalCount = count - 1;
NSLog(@"%@賣出去了一張票,還剩下%zd張票",[NSThread currentThread].name,self.totalCount);
}else
{
NSLog(@"票已經(jīng)賣光了,做灰機(jī)回去吧~");
break;
}
}
注意點:
1)鎖定一份代碼只能使用同一把鎖,
2)加鎖的時候要注意位置
3)加鎖有前提條件:只有當(dāng)多個線程存在搶奪同一塊資源的時候才需要加鎖
4)加鎖是需要耗費性能的
5)造成線程同步(多個線程按照固定的順序來執(zhí)行任務(wù))
加同步鎖 (NSLock)
舉例
NSLock *lock = [[NSLock alloc] init];
[lock lock];
//1.檢查余票
NSInteger count = self.totalCount;
if (count >0) {
for (NSInteger i = 0; i<100000; i++) {
//演示耗時操作
}
//2.賣出去一張票
self.totalCount = count - 1;
NSLog(@"%@賣出去了一張票,還剩下%zd張票",[NSThread currentThread].name,self.totalCount);
}else
{
NSLog(@"票已經(jīng)賣光了,做灰機(jī)回去吧~");
break;
}
[lock unlock];
AFNetWorking中用NSLock比較多一點 可以在AFNetWorking中搜索unlock方法進(jìn)行查看具體使用
其它的加鎖方式還有:
NSRecursiveLock :遞歸鎖,有時候“加鎖代碼”中存在遞歸調(diào)用排龄,遞歸開始前加鎖波势,遞歸調(diào)用開始后會重復(fù)執(zhí)行此方法以至于反復(fù)執(zhí)行加鎖代碼最終造成死鎖,這個時候可以使用遞歸鎖來解決橄维。使用遞歸鎖可以在一個線程中反復(fù)獲取鎖而不造成死鎖尺铣,這個過程中會記錄獲取鎖和釋放鎖的次數(shù),只有最后兩者平衡鎖才被最終釋放争舞。
NSDistributedLock:分布鎖凛忿,它本身是一個互斥鎖,基于文件方式實現(xiàn)鎖機(jī)制竞川,可以跨進(jìn)程訪問店溢。
pthread_mutex_t:同步鎖,基于C語言的同步鎖機(jī)制委乌,使用方法與其他同步鎖機(jī)制類似床牧。
- 原子性和非原子性
我們平時在開發(fā)中定義屬性時有以下兩種選擇
atomic:原子性,線程相對來說是安全的,因為它會為該屬性的setter方法加鎖,但是性能會影響
nonatomic:非原子性,線程是不安全的,不會為setter方法加鎖.性能會好
關(guān)于這兩個關(guān)鍵字的具體介紹參考下面的文章:
https://www.douban.com/note/486901956/
- 線程間通訊
舉個下載圖片的例子
GCD:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSURL *url = [NSURL URLWithString:@"http://dimg07.c- ctrip.com/images/tg/946/212/497/81b56770ed4544a6a8a1125fb381753d_C_640_640.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
dispatch_sync(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
NSThread:
//開線程
[NSThread detachNewThreadSelector:@selector(download) toTarget:self withObject:nil];
download
// 具體下載
NSURL *url = [NSURL URLWithString:@"http://dimg07.c-ctrip.com/images/tg/946/212/497/81b56770ed4544a6a8a1125fb381753d_C_640_640.jpg"];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:imageData];
NSLog(@"dowbnload=====%@",[NSThread currentThread]);
//4.回到主線程設(shè)置圖片
/*
第一個參數(shù):回到主線程要調(diào)用的方法
第二個參數(shù):回到主線程需要傳遞的參數(shù) image
第三個參數(shù):要不要等待調(diào)用方法結(jié)束之后再繼續(xù)往下走
*/
//[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:NO];
//NSLog(@"---end----");
[self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
// [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];