GCD 文檔的查看
GCD 最全的資料不在百度鞋喇,不在 api ,在 mac 終端卧蜓。
打開終端
$ man dispatch
// 退出當(dāng)前界面
$ q
see also
我們可以通過輸入 man dispatch_after
查看相應(yīng)的文檔帐要。
// dispatch_after 函數(shù)的文檔
$ man dispatch_after
// dispatch_async 函數(shù)的文檔
$ man dispatch_async
1、隊列同步任務(wù)和異步任務(wù)在項目中的使用
假設(shè): a 為登錄弥奸, b宠叼, c 為不同的下載任務(wù)。
下面這樣的操作就可以保證其爵,只有在登錄的情況下才可以進(jìn)行 下載任務(wù)冒冬。
// 創(chuàng)建一個并發(fā)隊列
dispatch_queue_t q = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);
// 并發(fā)隊列同步執(zhí)行
// 不會開線程,會在當(dāng)前線程執(zhí)行
// 同步執(zhí)行任務(wù) - 作用可以讓一些異步執(zhí)行的任務(wù)"依賴" 某一個特殊的任務(wù)
dispatch_sync(q, ^{
NSLog(@"a: %@",[NSThread currentThread]);
});
// 永遠(yuǎn)都是在 a 執(zhí)行完畢后摩渺,在回執(zhí)行 b c 简烤,但是 b , c 執(zhí)行的順序是不定的。
// 并發(fā)隊列異步執(zhí)行
// 會開線程摇幻,在子線程運(yùn)行
dispatch_async(q, ^{
NSLog(@"b: %@",[NSThread currentThread]);
});
dispatch_async(q, ^{
NSLog(@"c: %@",[NSThread currentThread]);
});
打印結(jié)果:
2016-06-21 12:22:01.900 Thread-Objc[44983:2486117] a: <NSThread: 0x7f8762501ba0>{number = 1, name = main}
2016-06-21 12:22:01.900 Thread-Objc[44983:2486394] c: <NSThread: 0x7f876257ac90>{number = 3, name = (null)}
2016-06-21 12:22:01.900 Thread-Objc[44983:2486385] b: <NSThread: 0x7f8762717f20>{number = 2, name = (null)}
2横侦、隊列迭代
提交一個任務(wù)到隊列進(jìn)行多次執(zhí)行
/*
size_t iterations : 任務(wù)執(zhí)行的次數(shù)
dispatch_queue_t queue : 要執(zhí)行任務(wù)的隊列
void (^block)(size_t) : 要執(zhí)行的任務(wù)
*/
void
dispatch_apply(size_t iterations, dispatch_queue_t queue,
void (^block)(size_t));
// 上面函數(shù)是下面的封裝。
void
dispatch_apply_f(size_t iterations, dispatch_queue_t queue,
void *context,
void (*work)(void *, size_t));
如果 dispatch_queue_t queue 的隊列是并發(fā)隊列绰姻。任務(wù)是并發(fā)執(zhí)行的, 它必須是可重入的安全枉侧。
dispatch_apply( ) 是同步執(zhí)行的。 意味著會阻塞當(dāng)前線程狂芋。和 for 循環(huán)一樣只有在迭代完成后才會返回榨馁。如果需要異步執(zhí)行,就需要創(chuàng)建一個新的 隊列帜矾,調(diào)用 dispatch_async() 函數(shù)翼虫,再在 dispatch_async() 函數(shù)中調(diào)用 dispatch_apply( )。
示例:
NSLog(@"before: %@", [NSThread currentThread]);
__block int x = 0;
// q 是串行或者并行沒有多大的關(guān)系
dispatch_queue_t q = dispatch_queue_create("q", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(q, ^{
// q1 是串行的屡萤,就只開1條線程珍剑,并發(fā)的就開多條線程。線程數(shù)由 GCD 決定死陆。
dispatch_queue_t q1 = dispatch_queue_create("q1", DISPATCH_QUEUE_SERIAL);
size_t iterations = 10;
dispatch_apply( iterations, q1, ^(size_t idx ) {
NSLog(@"size_t : %zd --- x : %zd -- %@", idx, x, [NSThread currentThread]);
});
});
NSLog(@"after: %@", [NSThread currentThread]);
在主線程中調(diào)用 dispatch_apply( ) 傳入的是主隊列招拙,會造成死鎖。
文檔中說:
dispatch_apply() 是 dispatch_async() 和 semaphore (完成信號)的包裝;
(我特么真沒看出來)
dispatch_apply() 和 dispatch_async() 不一樣的地方是别凤,block 提交到 dispatch_apply() 是相對獨(dú)立的劈愚,或者是依賴于已經(jīng)完成但是 index 比自己小的 block 。
這個函數(shù)就像 for 循環(huán)一樣提供數(shù)據(jù)級并發(fā)闻妓。 相對于 for 循環(huán)(for 循序操作都是在當(dāng)前線程),并發(fā)迭代 的效率更加的高掠械。
使用前提條件:
本次的迭代執(zhí)行的 block 和上一次迭代執(zhí)行的 block 不能有任何的關(guān)系由缆。
(每次 block 的迭代執(zhí)行是相對獨(dú)立的!猾蒂。 如果說執(zhí)行的 block 之間有依賴關(guān)系均唉,那你還是放棄吧!)
如果你使用循環(huán)執(zhí)行固定次數(shù)的迭代, 并發(fā)dispatch queue可能會提高性能肚菠。
- 如果每次迭代執(zhí)行的任務(wù)與其它迭代獨(dú)立無關(guān),而且循環(huán)迭代執(zhí)行順序也無關(guān)緊要的話,你可以調(diào)用dispatch_apply或dispatch_apply_f函數(shù)來替換循環(huán)舔箭。
普通 for 循環(huán)
NSLog(@"before: %@", [NSThread currentThread]);
int x = 0;
for (int index = 0 ; index < 10; index ++) {
NSLog(@"task: %@ --- index : %zd ---- x : %zd", [NSThread currentThread], index , x );
x++;
}
NSLog(@"after: %@", [NSThread currentThread]);
打印結(jié)果:
2016-06-23 14:55:04.706 Thread-Objc[12389:1352331] before: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}
2016-06-23 14:55:04.707 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main} --- index : 0 ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)} --- index : 1 ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352381] task: <NSThread: 0x7f97f2635530>{number = 3, name = (null)} --- index : 2 ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352375] task: <NSThread: 0x7f97f2635970>{number = 4, name = (null)} --- index : 3 ---- x : 0
2016-06-23 14:55:04.708 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main} --- index : 4 ---- x : 1
2016-06-23 14:55:04.708 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)} --- index : 5 ---- x : 2
2016-06-23 14:55:04.708 Thread-Objc[12389:1352381] task: <NSThread: 0x7f97f2635530>{number = 3, name = (null)} --- index : 6 ---- x : 3
2016-06-23 14:55:04.709 Thread-Objc[12389:1352375] task: <NSThread: 0x7f97f2635970>{number = 4, name = (null)} --- index : 7 ---- x : 4
2016-06-23 14:55:04.709 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main} --- index : 8 ---- x : 5
2016-06-23 14:55:04.710 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)} --- index : 9 ---- x : 6
2016-06-23 14:55:04.721 Thread-Objc[12389:1352331] after: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}
GCD 迭代
NSLog(@"before: %@", [NSThread currentThread]);
size_t iterations = 10;
__block int x = 0;
dispatch_apply( iterations , dispatch_get_global_queue(0, 0), ^(size_t idx) {
NSLog(@"task: %@ --- index : %zd ---- x : %zd", [NSThread currentThread], idx , x );
x++;
});
NSLog(@"after: %@", [NSThread currentThread]);
打印結(jié)果
2016-06-23 14:55:04.706 Thread-Objc[12389:1352331] before: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}
2016-06-23 14:55:04.707 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main} --- index : 0 ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)} --- index : 1 ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352381] task: <NSThread: 0x7f97f2635530>{number = 3, name = (null)} --- index : 2 ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352375] task: <NSThread: 0x7f97f2635970>{number = 4, name = (null)} --- index : 3 ---- x : 0
2016-06-23 14:55:04.708 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main} --- index : 4 ---- x : 1
2016-06-23 14:55:04.708 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)} --- index : 5 ---- x : 2
2016-06-23 14:55:04.708 Thread-Objc[12389:1352381] task: <NSThread: 0x7f97f2635530>{number = 3, name = (null)} --- index : 6 ---- x : 3
2016-06-23 14:55:04.709 Thread-Objc[12389:1352375] task: <NSThread: 0x7f97f2635970>{number = 4, name = (null)} --- index : 7 ---- x : 4
2016-06-23 14:55:04.709 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main} --- index : 8 ---- x : 5
2016-06-23 14:55:04.710 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)} --- index : 9 ---- x : 6
2016-06-23 14:55:04.721 Thread-Objc[12389:1352331] after: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}
對比發(fā)現(xiàn) x 的值在 GCD 迭代的過程中是凌亂的。
當(dāng)符合前提條件蚊逢,GCD 的迭代的效率更高层扶。
3、隊列的暫停和啟動操作
給目標(biāo)隊列添加管理對象
/*
dispatch_object_t object : 隊列管理對象
dispatch_queue_t queue : 被管理的隊列
*/
void
dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);
調(diào)度對象的相關(guān)知識
調(diào)度對象是通過共享函數(shù)去協(xié)調(diào)內(nèi)存管理烙荷,暫停镜会,取消,上下文指針终抽。
-
掛起操作
在調(diào)度隊列或者調(diào)度源被觸發(fā)的 blocks 能夠通過分別執(zhí)行dispatch_suspend()
和dispatch_resume()
函數(shù)進(jìn)行掛起和啟動操作戳表。另一些調(diào)度對象是不支持掛起操作的。
這個調(diào)度框架總是在執(zhí)行 block 之前檢查 block 的懸掛狀態(tài)昼伴。一個 block 在執(zhí)行期間對 block 做的改變是不會產(chǎn)生效果的匾旭。因此一個對象的掛起操作是異步的,除非它是通過執(zhí)行上下文從目標(biāo)隊列獲取的對象圃郊。把一個不是調(diào)度隊列或調(diào)度源的對象執(zhí)行掛起操作或啟用操作的操作結(jié)果是沒有定義的价涝。
重點(diǎn):
懸掛操作適用于調(diào)度對象生命周期的任何時候。包括 finalizer 函數(shù)持舆,取消回調(diào)飒泻。 掛起操作會使計數(shù)器加1 ,啟用操作會使計數(shù)器減一吏廉。因此泞遗,dispatch_suspend()
和dispatch_resume()
進(jìn)行成對調(diào)用,以保證對象最終能夠釋放席覆。一個調(diào)度對象在所有的引用釋放后進(jìn)行掛起操作的操作結(jié)果是沒有定義的史辙。
-
上下文指針
調(diào)度對象支持補(bǔ)充(追加)上下文指針。 上下文指針的值能夠分別通過dispatch_get_context()
和dispatch_set_context()
函數(shù)進(jìn)行獲取和更新。 當(dāng)調(diào)度對象所有的引用全部釋放的時候聊倔,如果上下文指針是非空的晦毙,通過dispatch_set_finalizer_f()
能給每一個可選的對象指定一個可以異步調(diào)用的終結(jié)函數(shù)。給應(yīng)用程序一個可以給調(diào)度自由關(guān)聯(lián)上下文數(shù)據(jù)的機(jī)會耙蔑。這個終結(jié)器將要運(yùn)行在和 調(diào)度對象綁定的目標(biāo)隊列上见妒。