介紹
Dispatch, also known as Grand Central Dispatch (GCD), contains language features, runtime libraries, and system enhancements that provide systemic, comprehensive improvements to the support for concurrent code execution on multicore hardware in macOS, iOS, watchOS, and tvOS.
優(yōu)勢(shì)
- GCD 會(huì)
自動(dòng)管理線程的生命周期 - RD 只需要將
指定任務(wù)以特定方式提交到相應(yīng)隊(duì)列
概念
指定任務(wù)
將要執(zhí)行的操作
typedef void (^dispatch_block_t)(void);
特定方式
同步執(zhí)行(sync)
- 等待同步任務(wù)結(jié)束后再繼續(xù)執(zhí)行當(dāng)前任務(wù)
- 兩個(gè)任務(wù)在同一線程執(zhí)行,
不具備開啟線程能力
同步執(zhí)行
異步執(zhí)行(async)
- 提交異步任務(wù)后
立即繼續(xù)執(zhí)行當(dāng)前任務(wù) -
兩個(gè)任務(wù)可以在不同線程中執(zhí)行,具備開啟線程能力
異步執(zhí)行
異步執(zhí)行雖然具有開啟新線程的能力,但是并不一定開啟新線程
相應(yīng)隊(duì)列
隊(duì)列概念
隊(duì)列是一種特殊的線性表,采用 FIFO(先進(jìn)先出)的原則

串行隊(duì)列
- 所有任務(wù)按順序依次執(zhí)行,結(jié)束順序固定
-
隊(duì)列頭部的任務(wù)等待上一個(gè)任務(wù)執(zhí)行完畢后才出隊(duì)列
串行隊(duì)列
并發(fā)隊(duì)列
- 所有任務(wù)可以同時(shí)執(zhí)行,結(jié)束順序
不固定 -
只要有可用線程,則隊(duì)列頭部任務(wù)將持續(xù)出隊(duì)列
并發(fā)隊(duì)列
隊(duì)列和線程沒有必然聯(lián)系,隊(duì)列只是一系列任務(wù)的容器,而線程才是執(zhí)行任務(wù)的載體。
所以,不要認(rèn)為串行隊(duì)列中的所有任務(wù)都在同一個(gè)線程中。
實(shí)戰(zhàn)
實(shí)戰(zhàn) 1
在主線程中執(zhí)行如下代碼
self.serial_queue = dispatch_queue_create("def.test.serial", DISPATCH_QUEUE_SERIAL);
NSLog(@"liyc: a thread %@", [NSThread currentThread]);
for (NSUInteger i = 0; i < 5; i++) {
dispatch_async(self.serial_queue, ^{
NSLog(@"liyc: %@ thread %@", @(i), [NSThread currentThread]);
});
}
NSLog(@"liyc: b thread %@", [NSThread currentThread]);
for (NSUInteger i = 5; i < 10; i++) {
dispatch_sync(self.serial_queue, ^{
NSLog(@"liyc: %@ thread %@", @(i), [NSThread currentThread]);
});
}
分析如下:
在主線程中提交五個(gè)異步任務(wù),由于是異步,所以繼續(xù)在主線程執(zhí)行當(dāng)前任務(wù)。第二個(gè)for循環(huán)將要添加五個(gè)同步任務(wù),此時(shí)需要等待新添加的同步任務(wù)執(zhí)行完畢,而在這之前隊(duì)列中已經(jīng)存在五個(gè)任務(wù),所以要先依次執(zhí)行完畢后才能繼續(xù)執(zhí)行。那么得到如下結(jié)果:
2019-08-22 16:04:19.610366+0800 ThreadDemo[81233:4928855] liyc: a thread <NSThread: 0x600001d213c0>{number = 1, name = main}
2019-08-22 16:04:19.610560+0800 ThreadDemo[81233:4928855] liyc: b thread <NSThread: 0x600001d213c0>{number = 1, name = main}
2019-08-22 16:04:19.610597+0800 ThreadDemo[81233:4928907] liyc: 0 thread <NSThread: 0x600001d54700>{number = 3, name = (null)}
2019-08-22 16:04:19.610699+0800 ThreadDemo[81233:4928907] liyc: 1 thread <NSThread: 0x600001d54700>{number = 3, name = (null)}
2019-08-22 16:04:19.610802+0800 ThreadDemo[81233:4928907] liyc: 2 thread <NSThread: 0x600001d54700>{number = 3, name = (null)}
2019-08-22 16:04:19.610895+0800 ThreadDemo[81233:4928907] liyc: 3 thread <NSThread: 0x600001d54700>{number = 3, name = (null)}
2019-08-22 16:04:19.610999+0800 ThreadDemo[81233:4928907] liyc: 4 thread <NSThread: 0x600001d54700>{number = 3, name = (null)}
2019-08-22 16:04:19.611128+0800 ThreadDemo[81233:4928855] liyc: 5 thread <NSThread: 0x600001d213c0>{number = 1, name = main}
2019-08-22 16:04:19.611225+0800 ThreadDemo[81233:4928855] liyc: 6 thread <NSThread: 0x600001d213c0>{number = 1, name = main}
2019-08-22 16:04:19.611310+0800 ThreadDemo[81233:4928855] liyc: 7 thread <NSThread: 0x600001d213c0>{number = 1, name = main}
2019-08-22 16:04:19.759665+0800 ThreadDemo[81233:4928855] liyc: 8 thread <NSThread: 0x600001d213c0>{number = 1, name = main}
2019-08-22 16:04:19.759772+0800 ThreadDemo[81233:4928855] liyc: 9 thread <NSThread: 0x600001d213c0>{number = 1, name = main}
問題:
b一定緊隨a之后輸出嗎?
回答:不是的。大家看到1~4在子線程中,所以理論上b有可能在a之后5之前的任意位置。
大家感興趣的話可以自己測(cè)試一下,在兩個(gè)for循環(huán)之間加個(gè)耗時(shí)操作。
實(shí)戰(zhàn) 2
在主線程中執(zhí)行如下代碼
self.serial_queue = dispatch_get_main_queue();
NSLog(@"liyc: a thread %@", [NSThread currentThread]);
for (NSUInteger i = 0; i < 5; i++) {
dispatch_async(self.serial_queue, ^{
NSLog(@"liyc: %@ thread %@", @(i), [NSThread currentThread]);
});
}
NSLog(@"liyc: b thread %@", [NSThread currentThread]);
for (NSUInteger i = 5; i < 10; i++) {
dispatch_sync(self.serial_queue, ^{
NSLog(@"liyc: %@ thread %@", @(i), [NSThread currentThread]);
});
}
分析如下:
在主線程中提交五個(gè)異步任務(wù),由于是異步,所以繼續(xù)在主線程執(zhí)行當(dāng)前任務(wù)。第二個(gè)for循環(huán)將要添加五個(gè)同步任務(wù),此時(shí)需要等待新添加的同步任務(wù)執(zhí)行完畢,而在這之前隊(duì)列中已經(jīng)存在五個(gè)任務(wù),所以要先依次執(zhí)行完畢后才能繼續(xù)執(zhí)行。
以上與剛才一樣,但是,問題來了:當(dāng)前任務(wù)是在主隊(duì)列里出來的,并沒有執(zhí)行完畢,此時(shí)又想將主隊(duì)列里的頭部任務(wù)取出,而主隊(duì)列是串行隊(duì)列,取出隊(duì)列頭部又需要已經(jīng)出隊(duì)列的任務(wù)執(zhí)行完畢。造成相互等待,程序無法繼續(xù)執(zhí)行。那么得到如下結(jié)果:
2019-08-22 16:12:23.248246+0800 ThreadDemo[81337:4935835] liyc: a thread <NSThread: 0x600003ac1dc0>{number = 1, name = main}
2019-08-22 16:12:23.248468+0800 ThreadDemo[81337:4935835] liyc: b thread <NSThread: 0x600003ac1dc0>{number = 1, name = main}
實(shí)戰(zhàn) 3
在主線程中執(zhí)行如下代碼
self.concurrent_queue = dispatch_queue_create("def.test.concurrent", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"liyc: c thread %@", [NSThread currentThread]);
for (NSUInteger i = 10; i < 15; i++) {
dispatch_sync(self.concurrent_queue, ^{
NSLog(@"liyc: %@ thread %@", @(i), [NSThread currentThread]);
});
}
NSLog(@"liyc: d thread %@", [NSThread currentThread]);
for (NSUInteger i = 15; i < 20; i++) {
dispatch_async(self.concurrent_queue, ^{
NSLog(@"liyc: %@ thread %@", @(i), [NSThread currentThread]);
});
}
分析如下:
將要在主線程中提交五個(gè)同步任務(wù),由于是同步,所以五個(gè)任務(wù)依次被加入隊(duì)列,并且執(zhí)行。之后異步加入并發(fā)隊(duì)列,所以亂序的情況下在不同線程里被執(zhí)行。那么得到如下結(jié)果:
2019-08-22 16:27:17.104961+0800 ThreadDemo[81497:4949380] liyc: c thread <NSThread: 0x600001b692c0>{number = 1, name = main}
2019-08-22 16:27:17.105134+0800 ThreadDemo[81497:4949380] liyc: 10 thread <NSThread: 0x600001b692c0>{number = 1, name = main}
2019-08-22 16:27:17.105239+0800 ThreadDemo[81497:4949380] liyc: 11 thread <NSThread: 0x600001b692c0>{number = 1, name = main}
2019-08-22 16:27:17.105332+0800 ThreadDemo[81497:4949380] liyc: 12 thread <NSThread: 0x600001b692c0>{number = 1, name = main}
2019-08-22 16:27:17.105434+0800 ThreadDemo[81497:4949380] liyc: 13 thread <NSThread: 0x600001b692c0>{number = 1, name = main}
2019-08-22 16:27:17.105523+0800 ThreadDemo[81497:4949380] liyc: 14 thread <NSThread: 0x600001b692c0>{number = 1, name = main}
2019-08-22 16:27:17.105625+0800 ThreadDemo[81497:4949380] liyc: d thread <NSThread: 0x600001b692c0>{number = 1, name = main}
2019-08-22 16:27:17.105767+0800 ThreadDemo[81497:4949454] liyc: 15 thread <NSThread: 0x600001b0cdc0>{number = 3, name = (null)}
2019-08-22 16:27:17.105778+0800 ThreadDemo[81497:4949452] liyc: 16 thread <NSThread: 0x600001b02d00>{number = 4, name = (null)}
2019-08-22 16:27:17.105815+0800 ThreadDemo[81497:4949453] liyc: 17 thread <NSThread: 0x600001b60100>{number = 5, name = (null)}
2019-08-22 16:27:17.105821+0800 ThreadDemo[81497:4949451] liyc: 19 thread <NSThread: 0x600001b287c0>{number = 6, name = (null)}
2019-08-22 16:27:17.105834+0800 ThreadDemo[81497:4949464] liyc: 18 thread <NSThread: 0x600001b02ac0>{number = 7, name = (null)}
問題:
d一定在14之后輸出嗎?
回答:是的。因?yàn)樯线吺峭饺蝿?wù)。
實(shí)戰(zhàn) 4
在主線程中執(zhí)行如下代碼
self.concurrent_queue = dispatch_queue_create("def.test.concurrent", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"liyc: c thread %@", [NSThread currentThread]);
for (NSUInteger i = 10; i < 15; i++) {
NSLog(@"liyc: i-%@ thread %@", @(i), [NSThread currentThread]);
dispatch_sync(self.concurrent_queue, ^{
NSLog(@"liyc: %@ thread %@", @(i), [NSThread currentThread]);
});
}
NSLog(@"liyc: d thread %@", [NSThread currentThread]);
for (NSUInteger i = 15; i < 20; i++) {
NSLog(@"liyc: i-%@ thread %@", @(i), [NSThread currentThread]);
dispatch_async(self.concurrent_queue, ^{
NSLog(@"liyc: %@ thread %@", @(i), [NSThread currentThread]);
});
}
分析如下:
這里和實(shí)戰(zhàn) 3的區(qū)別是在for循環(huán)里添加了i-輸出。主要是理解for循環(huán)是連續(xù)執(zhí)行,還是等待加入的任務(wù)執(zhí)行完畢后再繼續(xù)執(zhí)行循環(huán)。那么得到如下結(jié)果:
2019-08-22 16:43:37.960402+0800 ThreadDemo[81711:4964687] liyc: c thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.960534+0800 ThreadDemo[81711:4964687] liyc: i-10 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.960652+0800 ThreadDemo[81711:4964687] liyc: 10 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.960768+0800 ThreadDemo[81711:4964687] liyc: i-11 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.960911+0800 ThreadDemo[81711:4964687] liyc: 11 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.961027+0800 ThreadDemo[81711:4964687] liyc: i-12 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.961127+0800 ThreadDemo[81711:4964687] liyc: 12 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.961254+0800 ThreadDemo[81711:4964687] liyc: i-13 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.961358+0800 ThreadDemo[81711:4964687] liyc: 13 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.961474+0800 ThreadDemo[81711:4964687] liyc: i-14 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.961581+0800 ThreadDemo[81711:4964687] liyc: 14 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.961690+0800 ThreadDemo[81711:4964687] liyc: d thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.966989+0800 ThreadDemo[81711:4964687] liyc: i-15 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.967175+0800 ThreadDemo[81711:4964687] liyc: i-16 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.967201+0800 ThreadDemo[81711:4964889] liyc: 15 thread <NSThread: 0x6000000b03c0>{number = 3, name = (null)}
2019-08-22 16:43:37.967363+0800 ThreadDemo[81711:4964687] liyc: i-17 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.967380+0800 ThreadDemo[81711:4964889] liyc: 16 thread <NSThread: 0x6000000b03c0>{number = 3, name = (null)}
2019-08-22 16:43:37.967492+0800 ThreadDemo[81711:4964687] liyc: i-18 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.967496+0800 ThreadDemo[81711:4964889] liyc: 17 thread <NSThread: 0x6000000b03c0>{number = 3, name = (null)}
2019-08-22 16:43:37.967625+0800 ThreadDemo[81711:4964889] liyc: 18 thread <NSThread: 0x6000000b03c0>{number = 3, name = (null)}
2019-08-22 16:43:37.967626+0800 ThreadDemo[81711:4964687] liyc: i-19 thread <NSThread: 0x6000000c13c0>{number = 1, name = main}
2019-08-22 16:43:37.967802+0800 ThreadDemo[81711:4964891] liyc: 19 thread <NSThread: 0x6000000bc000>{number = 4, name = (null)}
如果以上這些內(nèi)容全部理解了,那么關(guān)于GCD的使用問題應(yīng)該就不大了。實(shí)戰(zhàn)代碼看這里
其他
- dispatch_barrier_async
- dispatch_after
- dispatch_once
- dispatch_apply
- dispatch_group
- dispatch_group_notify
- dispatch_group_wait
- dispatch_group_enter/dispatch_group_leave
- dispatch_semaphore
最后
線程切換存在成本,需要酌情使用。但是如果只是把任務(wù)拋到子線程去處理,那么幾乎沒有開銷。所以大可以放心使用,比如,監(jiān)控、埋點(diǎn)相關(guān)操作,都可以放到子線程中執(zhí)行。



