iOS 多線程--GCD

前言:本文主要是關(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];
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市遭贸,隨后出現(xiàn)的幾起案子戈咳,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件著蛙,死亡現(xiàn)場離奇詭異删铃,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)册踩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門泳姐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人暂吉,你說我怎么就攤上這事胖秒。” “怎么了慕的?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵阎肝,是天一觀的道長。 經(jīng)常有香客問我肮街,道長风题,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任嫉父,我火速辦了婚禮沛硅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绕辖。我一直安慰自己摇肌,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布仪际。 她就那樣靜靜地躺著围小,像睡著了一般。 火紅的嫁衣襯著肌膚如雪树碱。 梳的紋絲不亂的頭發(fā)上肯适,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天,我揣著相機(jī)與錄音成榜,去河邊找鬼框舔。 笑死,一個胖子當(dāng)著我的面吹牛赎婚,可吹牛的內(nèi)容都是我干的雨饺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼惑淳,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了饺窿?” 一聲冷哼從身側(cè)響起歧焦,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后绢馍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體向瓷,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年舰涌,在試婚紗的時候發(fā)現(xiàn)自己被綠了猖任。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡瓷耙,死狀恐怖朱躺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情搁痛,我是刑警寧澤长搀,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站鸡典,受9級特大地震影響源请,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜彻况,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一谁尸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纽甘,春花似錦良蛮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至泽裳,卻和暖如春瞒斩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背涮总。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工胸囱, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瀑梗。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓烹笔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親抛丽。 傳聞我的和親對象是個殘疾皇子谤职,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 一、簡介在iOS所有實現(xiàn)多線程的方案中亿鲜,GCD應(yīng)該是最有魅力的允蜈,因為GCD本身是蘋果公司為多核的并行運算提出的解決...
    MYS_iOS_8801閱讀 572評論 0 0
  • 目錄:iOS多線程(一)--pthread、NSThreadiOS多線程(二)--GCD詳解iOS多線程(三)--...
    Claire_wu閱讀 1,075評論 0 6
  • 一、基本概念 線程是用來執(zhí)行任務(wù)的蛤克,線程徹底執(zhí)行完任務(wù)A才能執(zhí)行任務(wù)B捺癞,為了同時執(zhí)行兩個任務(wù),產(chǎn)生了多線程 1咖耘、進(jìn)...
    空白Null閱讀 676評論 0 3
  • -01- 有時忙完自已工作或生活上的事翘簇,一個人靜靜地坐在一個偏僻的角落,默默的感受著自已心中的這...
    宮岳霖閱讀 751評論 0 3
  • 原創(chuàng)2017-03-05我喊你敢答應(yīng)嗎 文 | 躲貓兒 看罷《虛無的十字架》最后幾頁,站起身晃了晃脖子夫否,我看到窗外...
    躲貓兒閱讀 455評論 0 0