GCD詳盡總結(jié)

GCD編程的核心就是dispatch隊列,dispatch block的執(zhí)行最終都會放進(jìn)某個隊列中去進(jìn)行蛛勉。gcd中相關(guān)函數(shù)的使用一般都是以dispatch開頭

一、函數(shù)和隊列

1、GCD 中的兩個常用函數(shù)
1讥珍、異步函數(shù)(async): 可以在新的線程中執(zhí)行任務(wù), 具備開啟新線程的能力;
2、同步函數(shù)(sync): 只能在當(dāng)前線程中執(zhí)行任務(wù), 不具備開新啟線程的能力窄瘟;

queue: 隊列  bolck: 任務(wù)
異步函數(shù)
dispatch_async(dispatch_queue_t queue, ^{
// block 內(nèi)容
});
同步函數(shù)
dispatch_sync(dispatch_queue_t queue, ^{
// block 內(nèi)容
});

2衷佃、GCD的隊列類型
1、并發(fā)隊列(Concurrent Dispatch Queue)
2蹄葱、串行隊列(Serial Dispatch Queue)

a.并發(fā)隊列:可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))氏义,并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效。

(并發(fā)的意思是可以同時執(zhí)行第一個任務(wù) 但是是時間是分片的图云,每刻只有一個任務(wù)執(zhí)行 就是執(zhí)行這個任務(wù)一會 再執(zhí)行另外任務(wù) 交替進(jìn)行的)

創(chuàng)建并發(fā)隊列
dispatch_queue_t queue = dispatch_queue_create("aaa", DISPATCH_QUEUE_CONCURRENT);
全局并發(fā)隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

b.串行隊列:讓任務(wù)一個接著一個的執(zhí)行 (一個任務(wù)執(zhí)行完畢再執(zhí)行下一個任務(wù))

創(chuàng)建串行隊列
dispatch_queue_t queue = dispatch_queue_create("aaa", DISPATCH_QUEUE_SERIAL);

獲取主隊列
//dispatch_get_main_queue()獲得主隊列
主隊列是一種特殊串行隊列惯悠;

和其它串行隊列一樣,這個隊列中的任務(wù)一次只能執(zhí)行一個竣况。
然而克婶,它能保證所有的任務(wù)都在主線程執(zhí)行,主線程是唯一可用于更新 UI 的線程丹泉。

二情萤、GCD 的基本使用

1. 異步函數(shù)+并發(fā)隊列:開啟多條線程,并發(fā)執(zhí)行任務(wù)
2. 異步函數(shù)+串行隊列:開啟一條線程摹恨,串行執(zhí)行任務(wù)
3. 異步函數(shù)+主隊列:不開線程筋岛,在主線程中串行執(zhí)行任務(wù)
4. 同步函數(shù)+并發(fā)隊列:不開線程,串行執(zhí)行任務(wù)
5. 同步函數(shù)+串行隊列:不開線程晒哄,串行執(zhí)行任務(wù)
6. 同步函數(shù)+主隊列:不開線程睁宰,串行執(zhí)行任務(wù)(注意死鎖發(fā)生)

1.異步函數(shù)和并發(fā)隊列

dispatch_queue_t queue = dispatch_queue_create("asyncConcurrent", DISPATCH_QUEUE_CONCURRENT);
// 也可以獲取全局并發(fā)隊列,執(zhí)行效果是一樣的
// dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{ NSLog(@"1 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"2 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"3 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"4 %@", [NSThread currentThread]); });

結(jié)果:
4 <NSThread: 0x60000026fd00>{number = 6, name = (null)}
2 <NSThread: 0x600000270e40>{number = 4, name = (null)}
1 <NSThread: 0x600000270f00>{number = 3, name = (null)}
3 <NSThread: 0x60000026c400>{number = 5, name = (null)}

可以看出隊列開啟了4條子線程區(qū)分別執(zhí)行4個任務(wù), 隊列中的任務(wù)是并發(fā)執(zhí)行的. 
但是在這里有個注意點: 并不是說有多少任務(wù), GCD就會開啟多少條線程,
具體開啟幾條線程是不確定的, 是由系統(tǒng)決定的.

2.異步函數(shù)和串行隊列

dispatch_queue_t queue = dispatch_queue_create("asyncSerial", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{ NSLog(@"1 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"2 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"3 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"4 %@", [NSThread currentThread]); });

結(jié)果:
1 <NSThread: 0x60000007e400>{number = 3, name = (null)}
2 <NSThread: 0x60000007e400>{number = 3, name = (null)}
3 <NSThread: 0x60000007e400>{number = 3, name = (null)}
4 <NSThread: 0x60000007e400>{number = 3, name = (null)}

隊列只開啟了一條子線程, 去一個接著一個任務(wù)去執(zhí)行. 這種方式對任務(wù)的執(zhí)行效率沒有任何提高.

3.異步函數(shù)和主隊列

dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{ NSLog(@"1 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"2 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"3 %@", [NSThread currentThread]); });
dispatch_async(queue, ^{ NSLog(@"4 %@", [NSThread currentThread]); });

結(jié)果:
1 <NSThread: 0x60000007a600>{number = 1, name = main}
2 <NSThread: 0x60000007a600>{number = 1, name = main}
3 <NSThread: 0x60000007a600>{number = 1, name = main}
4 <NSThread: 0x60000007a600>{number = 1, name = main}

主隊列所有的任務(wù)確實是在主線程執(zhí)行的, 雖然是異步函數(shù), 但也不會開啟線程.

4.同步函數(shù)和并發(fā)隊列

//第一個參數(shù): C語言的字符串,標(biāo)簽
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{ NSLog(@"test1- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"test2- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"test3- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"test4- %@", [NSThread currentThread]); });

打印結(jié)果:
test1- <NSThread: 0x608000078dc0>{number = 1, name = main}
test2- <NSThread: 0x608000078dc0>{number = 1, name = main}
test3- <NSThread: 0x608000078dc0>{number = 1, name = main}
test4- <NSThread: 0x608000078dc0>{number = 1, name = main}

同步函數(shù)是不會開啟子線程的, 所有任務(wù)都是在主線程中串行執(zhí)行的.

5.同步函數(shù)和串行隊列

dispatch_queue_t queue = dispatch_queue_create("syncSerial", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{ NSLog(@"syncSerial-1- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"syncSerial-2- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"syncSerial-3- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"syncSerial-4- %@", [NSThread currentThread]); });

打印結(jié)果:
syncSerial-1- <NSThread: 0x60800007b100>{number = 1, name = main}
syncSerial-2- <NSThread: 0x60800007b100>{number = 1, name = main}
syncSerial-3- <NSThread: 0x60800007b100>{number = 1, name = main}
syncSerial-4- <NSThread: 0x60800007b100>{number = 1, name = main}

同樣, 此時也是不會創(chuàng)建子線程的, 所有任務(wù)是在主線程中也是串行執(zhí)行, 和4的情況是一樣的效果.

6.同步函數(shù)和主隊列

NSLog(@"---begin---"); 
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{ NSLog(@"syncMain1- %@", [NSThread currentThread]); }); 
dispatch_sync(queue, ^{ NSLog(@"syncMain2- %@", [NSThread currentThread]); });
dispatch_sync(queue, ^{ NSLog(@"syncMain3- %@", [NSThread currentThread]); }); 
NSLog(@"---end---");

結(jié)果(只有這一行輸出,死鎖的情況):
---begin---

由隊列引起的循環(huán)等待揩晴。

三勋陪、GCD 常用函數(shù)

1. dispatch_barrier_async函數(shù)(柵欄函數(shù))

它等待所有位于barrier函數(shù)之前的操作執(zhí)行完畢后執(zhí)行, 并且在barrier函數(shù)執(zhí)行之后, barrier函數(shù)之后的操作才會得到執(zhí)行, 該函數(shù)需要同dispatch_queue_create函數(shù)生成的并發(fā)隊列(concurrent Dispatch Queue)一起使用

dispatch_barrier_async代碼實例:

//同dispatch_queue_create函數(shù)生成的concurrent Dispatch Queue隊列一起使用
dispatch_queue_t queue = dispatch_queue_create("123", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{NSLog(@"--1--%@", [NSThread currentThread]);});
dispatch_async(queue, ^{NSLog(@"--2--%@", [NSThread currentThread]);});
dispatch_barrier_async(queue, ^{NSLog(@"--barrier--%@", [NSThread currentThread]);});
dispatch_async(queue, ^{NSLog(@"--3--%@", [NSThread currentThread]);});
dispatch_async(queue, ^{NSLog(@"--4--%@", [NSThread currentThread]);});

輸出結(jié)果:1 2 --> barrier -->3 4  其中12 與 34 由于并行處理先后順序不定

下面是你何時會,和不會硫兰,使用障礙函數(shù)的情況:

自定義串行隊列:一個很壞的選擇诅愚;障礙不會有任何幫助,因為不管怎樣,一個串行隊列一次都只執(zhí)行一個操作违孝。

全局并發(fā)隊列: 要小心刹前,這可能不是最好的主意,因為其它系統(tǒng)可能在使用隊列而且你不能壟斷它們只為你自己的目的雌桑。

自定義并發(fā)隊列:這對于原子或臨界區(qū)代碼來說是極佳的選擇喇喉。任何你在設(shè)置或?qū)嵗男枰€程安全的事物都是使用障礙的最佳候選。

2. dispatch_after函數(shù)延遲處理任務(wù)
在實際的開發(fā)中,經(jīng)常會遇到想要在指定的時間間隔后執(zhí)行某個處理

2.1 默認(rèn)在主線程中執(zhí)行的

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
       NSLog(@"GCD-%@", [NSThread currentThread]); 
});
結(jié)果: GCD-<NSThread: 0x60000006b4c0>{number = 1, name = main}

2.2 可以修改任務(wù)任務(wù)執(zhí)行所在的線程. 任務(wù)是在子線程中執(zhí)行的.

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), queue, ^{
      NSLog(@"GCD-%@", [NSThread currentThread]); 
});
結(jié)果: GCD-<NSThread: 0x60000006a3c0>{number = 3, name = (null)}

何時適合使用 dispatch_after 比較合適校坑?
自定義串行隊列: 在一個自定義串行隊列上使用 dispatch_after 要小心拣技。你最好堅持使用主隊列。
主隊列(串行): 是使用 dispatch_after 的好選擇耍目;Xcode 提供了一個不錯的自動完成模版膏斤。
并發(fā)隊列: 在并發(fā)隊列上使用 dispatch_after 也要小心;你會這樣做就比較罕見邪驮。還是在主隊列做這些操作吧莫辨。

補充
NSObject中提供的線程延遲方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
通過NSTimer來延遲線程執(zhí)行
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];

3. once 一次性執(zhí)行

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
      NSLog(@"once - %@", [NSThread currentThread]);
});
它在整個運行程序中只會執(zhí)行一次, 
GCD 的一次性執(zhí)行代碼一般都是用在單例設(shè)計模式中.保證全局只有一個對象實例.

4. Group queue (隊列組):
將多線程進(jìn)行分組,最大的好處是可獲知所有線程的完成情況毅访。Group queue 可以通過調(diào)用dispatch_group_create()來獲取沮榜,通過dispatch_group_notify, 可以直接監(jiān)聽組里所有線程完成情況。

// 創(chuàng)建隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();
//隊列組異步執(zhí)行任務(wù)
dispatch_group_async(group, queue, ^{ NSLog(@"任務(wù)1--%@", [NSThread currentThread]); });
dispatch_group_async(group, queue, ^{ NSLog(@"任務(wù)2--%@", [NSThread currentThread]); });
dispatch_group_async(group, queue, ^{ NSLog(@"任務(wù)3--%@", [NSThread currentThread]); });
// 隊列組攔截通知模塊(內(nèi)部本身是異步執(zhí)行的,不會阻塞線程)
dispatch_group_notify(group, queue, ^{ NSLog(@"--隊列組任務(wù)執(zhí)行完畢--"); });

打印結(jié)果:
任務(wù)2--<NSThread: 0x60800007e140>{number = 8, name = (null)}
任務(wù)1--<NSThread: 0x60800007e040>{number = 6, name = (null)}
任務(wù)3--<NSThread: 0x60800007f6c0>{number = 9, name = (null)}
--隊列租任務(wù)執(zhí)行完畢--
任務(wù)1--<NSThread: 0x60800007f6c0>{number = 9, name = (null)}
任務(wù)2--<NSThread: 0x60800007e040>{number = 6, name = (null)}
任務(wù)3--<NSThread: 0x60800007e140>{number = 8, name = (null)}
--隊列租任務(wù)執(zhí)行完畢--
任務(wù)1--<NSThread: 0x60800007e040>{number = 6, name = (null)}
任務(wù)2--<NSThread: 0x60800007e140>{number = 8, name = (null)}
任務(wù)3--<NSThread: 0x60800007f6c0>{number = 9, name = (null)}
--隊列租任務(wù)執(zhí)行完畢--

4.1 dispatch_group_enter / leave
和內(nèi)存管理的引用計數(shù)類似喻粹,我們可以認(rèn)為group也持有一個整形變量(只是假設(shè))蟆融,當(dāng)調(diào)用enter時計數(shù)加1,調(diào)用leave時計數(shù)減1守呜,當(dāng)計數(shù)為0時會調(diào)用dispatch_group_notify并且dispatch_group_wait會停止等待振愿;

NSLog(@"1");
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_notify(group, queue, ^{
        NSLog(@"2");
});
    
dispatch_group_enter(group);
dispatch_sync(queue, ^{
        NSLog(@"3");
});
dispatch_group_leave(group);

輸出132
-------------------------------------------------
dispatch_group_t group =dispatch_group_create();
dispatch_queue_t globalQueue=dispatch_get_global_queue(0, 0);

dispatch_group_enter(group);
//模擬多線程耗時操作
dispatch_async(globalQueue, ^{
    sleep(5);
    NSLog(@"block1完成");
    dispatch_group_leave(group);
});

dispatch_group_enter(group);
//模擬多線程耗時操作
dispatch_async(globalQueue, ^{
    sleep(3);
    NSLog(@"block2完成");
    dispatch_group_leave(group);
});

dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
    NSLog(@"全部結(jié)束");
});

block2完成
block1完成
全部結(jié)束

block里執(zhí)行的是同步類型的代碼那么用dispatch_group_async一樣可以達(dá)到同步的效果,但是異步任務(wù)就不行了如下:

dispatch_queue_t dispatchQueue = dispatch_queue_create("ted.queue.next1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_group_t dispatchGroup = dispatch_group_create();
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
    dispatch_async(globalQueue, ^{
        sleep(3);
        NSLog(@"任務(wù)一完成");
    });
});

dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
    dispatch_async(globalQueue, ^{
        sleep(5);
        NSLog(@"任務(wù)二完成");
    });
});

dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
    NSLog(@"notify:任務(wù)都完成了");
});

如果dispatch_group_async里執(zhí)行的是異步代碼dispatch_group_notify會直接觸發(fā)而不會等待異步任務(wù)完成弛饭,而dispatch_group_enter冕末、和dispatch_group_leave則不會有這個問題,它們只需要在任務(wù)開始前enter結(jié)束后leave即可達(dá)到線程同步的效果侣颂。

dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
        dispatch_group_enter(dispatchGroup);
        dispatch_async(globalQueue, ^{
            sleep(5);
            NSLog(@"任務(wù)二完成");
            dispatch_group_leave(dispatchGroup);
        });
});

4.2 dispatch_group_wait
和dispatch_group_notify功能類似(多了一個dispatch_time_t參數(shù)可以設(shè)置超時時間)档桃,在group上任務(wù)完成前,dispatch_group_wait會阻塞當(dāng)前線程(所以不能放在主線程調(diào)用)一直等待憔晒;當(dāng)group上任務(wù)完成藻肄,或者等待時間超過設(shè)置的超時時間會結(jié)束等待;

5. GCD定時器
GCD定時器的優(yōu)勢
不受RunLoop的運行模式的影響(因為它的底層也是C語言)
大家在開發(fā)的過程中拒担,經(jīng)常會用到定時器嘹屯,通常的做法可能就是NSTimer,了解過GCD的同學(xué)可能會接觸到dispatch source的概念从撼,dispatch source是一個監(jiān)視某些類型事件的對象州弟。

1.dispatch_source_create
創(chuàng)建一個新的調(diào)度源來監(jiān)視低級別的系統(tǒng)對象和自動提交處理程序塊來響應(yīng)事件調(diào)度隊列

2.dispatch_source_set_timer
為一個定時源設(shè)置一個開始時間、事件間隔、誤差值
我們來看看這個函數(shù)原型
dispatch_source_set_timer(dispatch_source_t source,
                          dispatch_time_t start,
                          uint64_t interval,
                          uint64_t leeway);
source當(dāng)然就是我們第一步創(chuàng)建的調(diào)度源婆翔。
start是我們設(shè)定的計時開始時間拯杠,可以dispatch_time 和 dispatch_walltime 函數(shù)來創(chuàng)建它們.

3.dispatch_source_set_event_handler
給一個調(diào)度源設(shè)置一個時間處理塊。

4.dispatch_source_cancel
異步取消一個調(diào)度源啃奴,防止任何進(jìn)一步調(diào)用它的事件處理塊的發(fā)生

5.dispatch_source_set_cancel_handler
給一個調(diào)度源設(shè)置一個取消處理塊

6.dispatch_resume
同步等待一個對象潭陪,直到超時

以上幾個步驟中,dispatch_source_set_event_handler與dispatch_source_cancel必須一起出現(xiàn)最蕾,
否則調(diào)度源處理塊不會執(zhí)行依溯。

代碼示例:
//dispatch_walltime(NULL, 0)可以換成DISPATCH_TIME_NOW
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,queue);
dispatch_source_set_timer(_timer,
                          dispatch_walltime(NULL, 0),
                          1.0*NSEC_PER_SEC,
                          0); //每秒執(zhí)行
dispatch_source_set_event_handler(_timer, ^{
     //在這里執(zhí)行事件
     dispatch_source_cancel(_timer);
});
//開始執(zhí)行dispatch源
dispatch_resume(_timer);

6、快速迭代
使用dispatch_apply函數(shù)能進(jìn)行快速迭代遍歷

dispatch_async(dispatch_get_global_queue(0, 0), ^(){
        dispatch_apply(10 , dispatch_get_global_queue(0, 0), ^(size_t index){
            // 執(zhí)行10次代碼瘟则,index順序不確定
            NSLog(@"%ld", index);
        });
        NSLog(@"done");
});

輸出 index 順序不確定誓沸,因為它是并行執(zhí)行的(dispatch_get_global_queue是并行隊列),但是done是在以上操作完成后才會執(zhí)行壹粟,因此,它一般都是放在dispatch_async里面(異步)宿百。

實際上趁仙,這里 dispatch_apply如果換成串行隊列上,則會依次輸出index垦页,但這樣違背了我們想并行提高執(zhí)行效率的初衷雀费。

7、dispatch_semaphore
信號量基于計數(shù)器的一種多線程同步機(jī)制痊焊。在多個線程訪問共有資源時候盏袄,會因為多線程的特性而引發(fā)數(shù)據(jù)出錯的問題。

3個相關(guān)方法:

  1. dispatch_semaphore_t dispatch_semaphore_create(long value) 方法接收一個long類型的參數(shù), 返回一個dispatch_semaphore_t類型的信號量薄啥,值為傳入的參數(shù)
  2. dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)接收一個信號和時間值辕羽,若信號的信號量為0,則會阻塞當(dāng)前線程垄惧,直到信號量大于0或者經(jīng)過輸入的時間值刁愿;若信號量大于0,則會使信號量減1并返回到逊,程序繼續(xù)住下執(zhí)行
  3. dispatch_semaphore_signal(dispatch_semaphore_t dsema)使信號量加1并返回

7.1:保持線程同步

dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); 
__block int j = 0; 
dispatch_async(queue, ^{ 
      j = 100; 
      dispatch_semaphore_signal(semaphore); 
}); 
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 
NSLog(@"finish j = %zd", j);

結(jié)果輸出 j = 100铣口;
如果注掉dispatch_semaphore_wait這一行,則 j = 0觉壶;

解釋
由于是將block異步添加到一個并行隊列里面脑题,所以程序在主線程躍過block直接到dispatch_semaphore_wait這一行,因為semaphore信號量為0铜靶,時間值為DISPATCH_TIME_FOREVER叔遂,所以當(dāng)前線程會一直阻塞,直到block在子線程執(zhí)行到dispatch_semaphore_signal,使信號量+1掏熬,此時semaphore信號量為1了佑稠,所以程序繼續(xù)往下執(zhí)行。這就保證了線程間同步了旗芬。

7.2:為線程加鎖

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1); 
for (int i = 0; i < 100; i++) { 
      dispatch_async(queue, ^{ 
            // 相當(dāng)于加鎖 
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 
            NSLog(@"i = %zd semaphore = %@", i, semaphore); 
            // 相當(dāng)于解鎖 
            dispatch_semaphore_signal(semaphore); 
      }); 
}

注釋:當(dāng)線程1執(zhí)行到dispatch_semaphore_wait這一行時舌胶,semaphore的信號量為1,所以使信號量-1變?yōu)?疮丛,并且線程1繼續(xù)往下執(zhí)行幔嫂;如果當(dāng)在線程1NSLog這一行代碼還沒執(zhí)行完的時候,又有線程2來訪問誊薄,執(zhí)行dispatch_semaphore_wait時由于此時信號量為0履恩,且時間為DISPATCH_TIME_FOREVER,所以會一直阻塞線程2(此時線程2處于等待狀態(tài)),直到線程1執(zhí)行完NSLog并執(zhí)行完dispatch_semaphore_signal使信號量為1后呢蔫,線程2才能解除阻塞繼續(xù)住下執(zhí)行切心。以上可以保證同時只有一個線程執(zhí)行NSLog這一行代碼。

附:
GCD使用三部曲之:基本用法里面提到“掛起和恢復(fù)隊列‘’

dispatch_group的理解及使用
iOS 使用dispatch_group_enter使多次網(wǎng)絡(luò)請求依次執(zhí)行http://www.reibang.com/p/23d77601483d

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末片吊,一起剝皮案震驚了整個濱河市绽昏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌俏脊,老刑警劉巖全谤,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異爷贫,居然都是意外死亡认然,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門漫萄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來卷员,“玉大人,你說我怎么就攤上這事腾务∽庸危” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵窑睁,是天一觀的道長挺峡。 經(jīng)常有香客問我,道長担钮,這世上最難降的妖魔是什么橱赠? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮箫津,結(jié)果婚禮上狭姨,老公的妹妹穿的比我還像新娘宰啦。我一直安慰自己,他們只是感情好饼拍,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布赡模。 她就那樣靜靜地躺著,像睡著了一般师抄。 火紅的嫁衣襯著肌膚如雪漓柑。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天叨吮,我揣著相機(jī)與錄音辆布,去河邊找鬼。 笑死茶鉴,一個胖子當(dāng)著我的面吹牛锋玲,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播涵叮,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼惭蹂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了割粮?” 一聲冷哼從身側(cè)響起盾碗,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎穆刻,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體杠步,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡氢伟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了幽歼。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片朵锣。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖甸私,靈堂內(nèi)的尸體忽然破棺而出诚些,到底是詐尸還是另有隱情,我是刑警寧澤皇型,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布诬烹,位于F島的核電站,受9級特大地震影響弃鸦,放射性物質(zhì)發(fā)生泄漏绞吁。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一唬格、第九天 我趴在偏房一處隱蔽的房頂上張望家破。 院中可真熱鬧颜说,春花似錦、人聲如沸汰聋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烹困。三九已至玄妈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間韭邓,已是汗流浹背措近。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留女淑,地道東北人瞭郑。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像鸭你,于是被迫代替她去往敵國和親屈张。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

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