libdispatch-1271.120.2 下載
蘋果官方資源opensource
多線程相關(guān)文獻(xiàn):
iOS 多線程原理 - 線程與隊(duì)列底層
iOS 多線程原理 - GCD函數(shù)底層
iOS 線程底層 - 鎖
本章節(jié)探究:
1.了解進(jìn)程、線程
2.串行隊(duì)列和并發(fā)隊(duì)列
3.線程死鎖的原因
4.同步函數(shù) dispatch_sync 和 異步函數(shù) dispatch_async
5.面試題
6.自定義線程池思想
一、概念相關(guān)
1.進(jìn)程與線程
進(jìn)程:
進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序,每個(gè)進(jìn)程之間是獨(dú)立的,每個(gè)進(jìn)程均運(yùn)行在其專用的且受保護(hù)的內(nèi)存空間內(nèi) (通過“活動(dòng)監(jiān)視器”可以查看 Mac 系統(tǒng)中所開啟的進(jìn)程)。
線程:
線程是進(jìn)程的基本執(zhí)行單元,一個(gè)進(jìn)程的所有任務(wù)都在線程中執(zhí)行,進(jìn)程要想執(zhí)行任務(wù),必須得有線程,進(jìn)程至少要有一條線程,程序啟動(dòng)會(huì)默認(rèn)開啟一條線程,這條線程被稱為主線程或 UI 線程。
進(jìn)程與線程的關(guān)系:
- 一個(gè)線程可以創(chuàng)建和撤銷另一個(gè)線程;同一個(gè)進(jìn)程中的多個(gè)線程之間可以并發(fā)執(zhí)行。
- 相對(duì)進(jìn)程而言,線程是一個(gè)更加接近于執(zhí)行體的概念,它可以與同進(jìn)程中的其他線程共享數(shù)據(jù),但擁有自己的??臻g,擁有獨(dú)立的執(zhí)行序列。
- 所處環(huán)境:在操作系統(tǒng)中能同時(shí)運(yùn)行多個(gè)進(jìn)程(程序);而在同一個(gè)進(jìn)程(程序)中有多個(gè)線程同時(shí)執(zhí)行(通過
CPU調(diào)度,在每個(gè)時(shí)間片中只有一個(gè)線程執(zhí)行) - 地址空間:同一進(jìn)程的線程共享本進(jìn)程的地址空間,而進(jìn)程之間則是獨(dú)立的地址空間。
- 資源擁有:同一進(jìn)程內(nèi)的線程共享本進(jìn)程的資源如內(nèi)存、
I/O、cpu等,但是進(jìn)程之間的資源是獨(dú)立的。 - 執(zhí)行過程:每個(gè)獨(dú)立的進(jìn)程程有一個(gè)程序運(yùn)行的入口、順序執(zhí)行序列和程序入口。但是線程不能獨(dú)立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個(gè)線程執(zhí)行控制。
- 根本區(qū)別:進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的基本單位,而線程是操作系統(tǒng)進(jìn)行任務(wù)調(diào)度和執(zhí)行的最小單位。
2.線程的聲明周期

3.多線程
時(shí)間片的概念:CPU在多個(gè)任務(wù)直接進(jìn)行快速的切換,這個(gè)時(shí)間間隔就是時(shí)間片。
單核CPU同一時(shí)間,CPU只能處理 1 個(gè)線程上的任務(wù)。
多線程同時(shí)執(zhí)行:
CPU 快速的在多個(gè)線程之間的切換,CPU 調(diào)度線程的時(shí)間足夠快,就造成了多線程的“同時(shí)”執(zhí)行的效果;如果線程數(shù)非常多,CPU 會(huì)在 N 個(gè)線程之間切換,消耗大量的 CPU 資源,每個(gè)線程被調(diào)度的次數(shù)會(huì)降低,線程的執(zhí)行效率降低。
多線程的意義:
- 優(yōu)點(diǎn)
- 能適當(dāng)提高程序的執(zhí)行效率
- 能適當(dāng)提高資源的利用率(CPU,內(nèi)存)
- 線程上的任務(wù)執(zhí)行完成后,線程會(huì)自動(dòng)銷毀
- 缺點(diǎn)
- 開啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,主線程占1M,其它線程各占 512 KB)
- 如果開啟大量的線程,會(huì)占用大量的內(nèi)存空間,降低程序的性能
- 線程越多,CPU 在調(diào)用線程上的開銷就越大
- 程序設(shè)計(jì)更加復(fù)雜,比如線程間的通信、多線程的數(shù)據(jù)共享
開辟一條線程大概需要90微秒的時(shí)間。
// 獲取設(shè)備能夠支持線程的最大并發(fā)數(shù)量
NSLog(@"%ld", [NSProcessInfo processInfo].activeProcessorCount);
過多的開辟線程沒有意義
4.線程池
GCD內(nèi)部維護(hù)了一個(gè)線程池去管理64條線程,在App需要線程調(diào)度任務(wù)的時(shí)候?qū)崿F(xiàn)復(fù)用;當(dāng)前線程完成任務(wù)后就會(huì)被緩存到線程池里,下次再調(diào)用開辟線程的代碼,GCD會(huì)從線程池上找已經(jīng)開辟且就緒狀態(tài)的線程。
所以開辟線程的代碼,并不是真正意義上的開辟線程。盡管GCD線程池里已有64條線程,但是最大并發(fā)數(shù)量還得是 [NSProcessInfo processInfo].activeProcessorCount; 。


5.GCD
GCD全稱是Grand Central Dispatch,是蘋果公司為多核的并行運(yùn)算提出的解決方案,它是純 C 語言并提供了非常多強(qiáng)大的函數(shù);GCD會(huì)自動(dòng)利用更多的CPU內(nèi)核(比如雙核、四核);GCD會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)。
程序員只需要告訴 GCD 想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼。
6. 線程和Runloop的關(guān)系
- 1.
runloop與線程是一一對(duì)應(yīng)的,一個(gè)runloop對(duì)應(yīng)一個(gè)核心的線程,為什么說是核心的,是因?yàn)?code>runloop是可以嵌套的,但是核心的只能有一個(gè),他們的關(guān)系保存在一個(gè)全局的字典里 - 2.
runloop是來管理線程的,當(dāng)線程的runloop被開啟后,線程會(huì)在執(zhí)行完任務(wù)后進(jìn)入休眠狀態(tài),有了任務(wù)就會(huì)被喚醒去執(zhí)行任務(wù) - 3.
runloop在第一次獲取時(shí)被創(chuàng)建,在線程結(jié)束時(shí)被銷毀 - 4.對(duì)于
主線程來說,runloop在程序一啟動(dòng)就默認(rèn)創(chuàng)建好了 - 5.對(duì)于
子線程來說,runloop是懶加載的,只有當(dāng)我們使用的時(shí)候才會(huì)創(chuàng)建,所以在子線程用定時(shí)器要注意:確保子線程的runloop被創(chuàng)建,不然定時(shí)器不會(huì)回調(diào)
二、串行隊(duì)列 與 并發(fā)隊(duì)列
隊(duì)列和線程沒有任何關(guān)系,隊(duì)列是存儲(chǔ)任務(wù)的,線程是從隊(duì)列中取出任務(wù)去執(zhí)行的。
隊(duì)列分四種:串行隊(duì)列、并發(fā)隊(duì)列、全局并發(fā)隊(duì)列、主隊(duì)列
隊(duì)列的特性:先進(jìn)先出 FIFO
- 獲取主隊(duì)列:
dispatch_get_main_queue
DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_CONST DISPATCH_NOTHROW
dispatch_queue_main_t
dispatch_get_main_queue(void)
{
return DISPATCH_GLOBAL_OBJECT(dispatch_queue_main_t, _dispatch_main_q); // _dispatch_main_q
}
DISPATCH_GLOBAL_OBJECT是一個(gè)宏定義好多地方有,沒有辦法定位到實(shí)際調(diào)用的哪個(gè)宏定義。但是通過lldb打印堆棧bt的話,又會(huì)多出好多別的函數(shù)調(diào)用不相關(guān)的東西。
打印主線程,它會(huì)有一個(gè)特定的名稱:com.apple.main-thread
NSLog(@"%@",dispatch_get_main_queue());
// <OS_dispatch_queue_main: com.apple.main-thread>
源碼里全局搜主線程名稱,就能找到main_queue的初始化的地方

主隊(duì)列是串行隊(duì)列的一個(gè)標(biāo)志性的東西:DQF_WIDTH(1)
在隊(duì)列創(chuàng)建的時(shí)候看看源碼就知道了。
- 創(chuàng)建隊(duì)列:
dispatch_queue_create
// label: 隊(duì)列名稱 attr是串行隊(duì)列還是并發(fā)列表
dispatch_queue_t
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)
{
return _dispatch_lane_create_with_target(label, attr,
DISPATCH_TARGET_QUEUE_DEFAULT, true); // DISPATCH_TARGET_QUEUE_DEFAULT = NULL
}
只需要關(guān)心第二個(gè)參數(shù)attr對(duì)于串行與并發(fā)的區(qū)別,它在_dispatch_lane_create_with_target的形參名稱是dqa。

把dqa封裝成了dqai,它是怎么封裝的?

過多的不需要太關(guān)注拉。
再回來看看_dispatch_lane_create_with_target初始化隊(duì)列的步驟:
1.規(guī)范化參數(shù) (qos, overcommit, tq)

2.初始化隊(duì)列

初始化的時(shí)候會(huì)判斷串行并發(fā)標(biāo)志位去限制width是多少,串行指定是1,并發(fā)是14

來看看_dispatch_queue_init的隊(duì)列初始化,我們關(guān)注的隊(duì)列是串行和并發(fā)的根本區(qū)別就是DQF_WIDTH(width),串行是DQF_WIDTH(1)

而dq->dq_serialnum它其實(shí)是標(biāo)志是這個(gè)隊(duì)列是什么隊(duì)列

串行隊(duì)列與并發(fā)隊(duì)列區(qū)別實(shí)質(zhì)的
總結(jié):
DQF_WIDTH(1)-串行隊(duì)列- 舉例:?jiǎn)涡械?/strong>
DQF_WIDTH(>1)-并發(fā)隊(duì)列- 舉例:多車道
ps: 可以把隊(duì)列看成是工廠流水線,保存著需要加工的部件,線程就是完成部件的工人。一條流水線有幾個(gè)部件道就是串行隊(duì)列與并發(fā)隊(duì)列的區(qū)別。
三、線程死鎖的原因
先來看看這個(gè)死鎖現(xiàn)象
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{ // 這里產(chǎn)生了死鎖
NSLog(@"這里不會(huì)來了");
});
NSLog(@"2");
}
造成線程死鎖的原因:
NSLog(@"2");的任務(wù)需要等待dispatch_sync里的任務(wù)執(zhí)行完才能執(zhí)行,而dispatch_sync里的任務(wù)是最后加入到主隊(duì)列的,需要等待NSLog(@"2");執(zhí)行完才會(huì)執(zhí)行。相互等待造成死鎖。
崩潰的信息也有展示出來:


打開libdispatch源碼
搜索這個(gè)崩潰信息:__DISPATCH_WAIT_FOR_QUEUE__

可以清晰看到造成線程死鎖會(huì)通過這個(gè)if條件判斷,解開這個(gè)條件判斷相當(dāng)于看清了造成死鎖崩潰的本質(zhì)了。(其實(shí)這段提示信息就已經(jīng)解釋了線程死鎖的原因:dispatch_sync called on queue already owned by current thread)
_dq_state_drain_locked_by的源碼聲明:

_dispatch_lock_is_locked_by的源碼聲明:

要產(chǎn)生死鎖(這個(gè)函數(shù)返回true)必須是lock_value與tid是相等。
造成線程死鎖的總結(jié):
在和當(dāng)前隊(duì)列相關(guān)的線程 同步地 向串行隊(duì)列添加任務(wù),就會(huì)產(chǎn)生死鎖。
死鎖的必備條件:1.線程同步 2.串行隊(duì)列
- 死鎖案例:
- (void)viewDidLoad {
[super viewDidLoad];
// dispatch_sync不具備開辟線程的能力,所以一直在主線程工作。
dispatch_queue_t q = dispatch_queue_create("AnAn", DISPATCH_QUEUE_SERIAL);
dispatch_sync(q, ^{
NSLog(@"%@", [NSThread currentThread]); // main 主線程的環(huán)境是在q隊(duì)列里
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{ // 死鎖
NSLog(@"2"); // 主線程的環(huán)境是在主隊(duì)列里,所以死鎖了
});
NSLog(@"3");
});
NSLog(@"4");
}
// main 1 死鎖
四、同步函數(shù) 與 異步函數(shù)
看同/異步函數(shù)的源碼我們關(guān)注的點(diǎn):
1.任務(wù)(block)的調(diào)用時(shí)機(jī)
2.關(guān)于線程相關(guān)的操作
- 同步函數(shù)
dispatch_sync

_dispatch_Block_invoke其實(shí)就是任務(wù)(block)封裝成Block_layout結(jié)構(gòu)體:

接下來需要關(guān)注_dispatch_sync_f函數(shù)的第三個(gè)參數(shù)就是我們的任務(wù)(func),它是什么時(shí)候執(zhí)行的。
_dispatch_sync_f的源碼聲明:

_dispatch_sync_f_inline的源碼聲明:

_dispatch_sync_f_inline里面有很多個(gè)地方進(jìn)行if條件判斷并使用了func
這是因?yàn)殛?duì)列參數(shù)dq有四種 主隊(duì)列/串行隊(duì)列/并發(fā)隊(duì)列/全局并發(fā)隊(duì)列 導(dǎo)致有很多種分支
由于libdispatch源碼是沒辦法編譯的,所以我們可以在新建工程demo,并且在使用同步函數(shù)dispatch_sync時(shí)打上符號(hào)斷點(diǎn),哪里使用了func就打上哪個(gè)符號(hào),就可以攔截func在不同情況下的去了哪個(gè)分支了。(也可以使用lldb的調(diào)試命令bt看看func的去向)
以 global_queue + dispatch_sync 組合為例,進(jìn)行調(diào)試


走到了_dispatch_sync_f_slow分支,再來看看這個(gè)函數(shù)的源碼
_dispatch_sync_f_slow的源碼聲明:

demo上繼續(xù)打上這倆函數(shù)符號(hào)斷點(diǎn),繼續(xù)走

_dispatch_sync_function_invoke的源碼聲明:

_dispatch_client_callout的源碼聲明:

到這里dispatch_sync的執(zhí)行就結(jié)束了,別的組合有興趣可以自己去試試。
回憶我們的關(guān)注點(diǎn),在看dispatch_sync源碼的時(shí)候,并沒有發(fā)現(xiàn)線程相關(guān)的操作,沒有發(fā)現(xiàn)對(duì)任務(wù)的保存操作,任務(wù)在一直傳遞到底層代碼后,立即被執(zhí)行。
dispatch_sync結(jié)論:
同步函數(shù)dispatch_sync :立即執(zhí)行、阻塞當(dāng)前線程、不具備開辟子線程的能力
- 異步函數(shù)
dispatch_async

-
_dispatch_continuation_init保存任務(wù),設(shè)置優(yōu)先級(jí)
_dispatch_continuation_init的源碼聲明:

_dispatch_continuation_init_f的源碼聲明:

_dispatch_continuation_priority_set是設(shè)置優(yōu)先級(jí),直接返回了qos:

可以看到_dispatch_continuation_init并沒有對(duì)線程和任務(wù)執(zhí)行的操作,僅僅只是保存了任務(wù),在需要的時(shí)候拿出來執(zhí)行。
_dispatch_continuation_async
_dispatch_continuation_async的源碼聲明:

dx_push是宏定義:
#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)
找到dq_push的聲明:

根據(jù)不同的隊(duì)列賦值給dq_push不一樣的函數(shù)
以并發(fā)隊(duì)列為例:
_dispatch_lane_concurrent_push的源碼聲明:

_dispatch_continuation_redirect_push的源碼聲明:

這里會(huì)發(fā)現(xiàn)又走到了dx_push,即遞歸了!綜合前面隊(duì)列創(chuàng)建時(shí)可知,隊(duì)列也是一個(gè)對(duì)象,有父類、根類,所以會(huì)遞歸執(zhí)行到根類的方法。
那do_targetq是什么呢?得回到隊(duì)列的創(chuàng)建dispatch_queue_create去查看:


在dispatch_queue_create的時(shí)候tq就賦值出來是_dispatch_get_root_queue了。
看看_dispatch_get_root_queue的源碼聲明:

回到_dispatch_continuation_redirect_push上面說它遞歸調(diào)用了dx_push,此時(shí)它的類型卻是dispatch_queue_global_t了。(dx_push是dq_push的宏定義)

進(jìn)去_dispatch_root_queue_push:

進(jìn)去_dispatch_root_queue_push_inline:

進(jìn)去_dispatch_root_queue_poke:

進(jìn)去_dispatch_root_queue_poke_slow:
DISPATCH_NOINLINE
static void
_dispatch_root_queue_poke_slow(dispatch_queue_global_t dq, int n, int floor)
{
int remaining = n;
int r = ENOSYS;
_dispatch_root_queues_init();//重點(diǎn)
...
//do-while循環(huán)創(chuàng)建線程
do {
_dispatch_retain(dq); // released in _dispatch_worker_thread
while ((r = pthread_create(pthr, attr, _dispatch_worker_thread, dq))) {
if (r != EAGAIN) {
(void)dispatch_assume_zero(r);
}
_dispatch_temporary_resource_shortage();
}
} while (--remaining);
...
}
走到了這里就進(jìn)行了線程的操作啦。
分析一下:_dispatch_root_queues_init
DISPATCH_STATIC_GLOBAL(dispatch_once_t _dispatch_root_queues_pred);
DISPATCH_ALWAYS_INLINE
static inline void
_dispatch_root_queues_init(void)
{
dispatch_once_f(&_dispatch_root_queues_pred, NULL,
_dispatch_root_queues_init_once);
}
發(fā)現(xiàn)是一個(gè)dispatch_once_f單例(下面會(huì)介紹單例),其中傳入的func是_dispatch_root_queues_init_once。
綜上所述,異步函數(shù)
dispatch_async的底層分析如下:【準(zhǔn)備工作】:首先,將異步任務(wù)拷貝并封裝,并設(shè)置回調(diào)函數(shù)
func。
【block回調(diào)】:底層通過dx_push遞歸,會(huì)重定向到根隊(duì)列,然后通過pthread_creat創(chuàng)建線程,最后通過dx_invoke執(zhí)行block回調(diào)(注意dx_push和dx_invoke是成對(duì)的)。
總結(jié)
dispatch_async中子線程創(chuàng)建的調(diào)用流程:
1.dispatch_async->_dispatch_continuation_async->dx_push->dq_push->并發(fā)隊(duì)列:_dispatch_lane_concurrent_push->_dispatch_continuation_redirect_push2.
_dispatch_continuation_redirect_push->dx_push(此時(shí)是global_queue) ->_dispatch_root_queue_push->_dispatch_root_queue_push_inline->_dispatch_root_queue_poke->_dispatch_root_queue_poke_slow->線程池調(diào)度,創(chuàng)建線程pthread_create
總結(jié)同/異步函數(shù)特性:
同步函數(shù)dispatch_sync :
1. 阻塞當(dāng)前線程進(jìn)?等待,直到當(dāng)前添加到隊(duì)列的任務(wù)執(zhí)?完成;
2. 只能在當(dāng)前線程執(zhí)?任務(wù),不具備開啟新線程的能?。異步函數(shù)
dispatch_async:
1. 不會(huì)阻塞線程,不需要等待,任務(wù)可以繼續(xù)執(zhí)?;
2. 可以在新的線程執(zhí)?任務(wù),具備開啟新線程的能?。(并發(fā)隊(duì)列可以開啟多條?線程,串?隊(duì)列只能開啟?條?線程)
五、面試題
ps: 注意要考慮任務(wù)復(fù)雜度
- (void)test1 {
dispatch_queue_t queue = dispatch_queue_create("AnAn", DISPATCH_QUEUE_CONCURRENT); // 并發(fā)隊(duì)列
NSLog(@"1");
dispatch_async(queue, ^{
// sleep(2);
NSLog(@"2");
dispatch_sync(queue, ^{
NSLog(@"3");
});
NSLog(@"4");
});
//sleep(2);
NSLog(@"5");
}
// 1最前面 2在3前面 3在4前面 2和5沒有順序
- (void)test2 {
dispatch_queue_t queue = dispatch_queue_create("AnAn", DISPATCH_QUEUE_SERIAL); // 串行隊(duì)列
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"2");
dispatch_sync(queue, ^{ // 這里死鎖
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}
// 1最先 2和5沒有順序 死鎖
- (void)test3 {
dispatch_queue_t queue = dispatch_queue_create("AnAn", DISPATCH_QUEUE_SERIAL); // 串行隊(duì)列
dispatch_async(queue, ^{
NSLog(@"1");
dispatch_async(queue, ^{
NSLog(@"2");
});
NSLog(@"3");
});
// sleep(3); // 萬一主線程這里復(fù)雜操作呢,把下面的任務(wù)延遲添加到queue隊(duì)列
dispatch_async(queue, ^{
// sleep(3);
NSLog(@"4");
});
}
// 13 4和2沒有順序
- (void)test4 {
self.num = 0;
while (self.num < 100) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.num ++;
});
}
NSLog(@"self.num = %d",self.num);
}
// 比100大一點(diǎn)
- (void)test5 {
self.num = 0;
for (int i = 0; i < 100; i ++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.num ++;
});
}
NSLog(@"self.num = %d",self.num);
}
// 0-100的其中一個(gè)數(shù)
-(void)test6 {
dispatch_queue_t queue = dispatch_queue_create("AnAn", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
// 阻塞主線程
dispatch_sync(queue, ^{
// sleep(2); // 主線程
NSLog(@"3");
});
// sleep(2);
NSLog(@"0");
dispatch_async(queue, ^{
NSLog(@"7");
});
dispatch_async(queue, ^{
NSLog(@"8");
});
dispatch_async(queue, ^{
NSLog(@"9");
});
}
// 12789在子線程;30在主線程;3一定在0之前執(zhí)行;789一定在30的后面執(zhí)行
-(void)test7 {
dispatch_queue_t t = dispatch_queue_create("AnAn", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_sync(t, ^{
NSLog(@"2");
dispatch_async(t, ^{
//sleep(2);
NSLog(@"3");
});
// sleep(2);
NSLog(@"4");
});
//sleep(2);
NSLog(@"5");
}
// 12一定先 5一定在4后面 3和5順序不一定 3和4順序不一定
-(void)test8 {
dispatch_queue_t t = dispatch_queue_create("AnAn", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"1");
dispatch_async(t, ^{
// sleep(2);
NSLog(@"2");
dispatch_sync(t, ^{
// sleep(2);
NSLog(@"3");
});
// sleep(2);
NSLog(@"4");
});
// sleep(2);
NSLog(@"5");
}
// 125順序不一定;3一定在2之后;4一定在3之后
-(void)test9 {
dispatch_queue_t t = dispatch_queue_create("lg", DISPATCH_QUEUE_SERIAL);
NSLog(@"1");
dispatch_sync(t, ^{
// 在主線程
NSLog(@"2");
dispatch_async(t, ^{
// sleep(2); // 子線程
NSLog(@"3");
});
sleep(2);
NSLog(@"4");
});
// sleep(2);
NSLog(@"5");
}
// 124一定先 35順序不一定
六、自定義線程池思想
通過分析YYKit的線程池進(jìn)行分析自己構(gòu)造一個(gè)線程池的思想。準(zhǔn)確來說YYKit的線程池應(yīng)該被叫做是隊(duì)列池。
核心思想:創(chuàng)建一個(gè)串行隊(duì)列數(shù)組,數(shù)組里的每一個(gè)隊(duì)列都進(jìn)行異步操作任務(wù),串行+異步=開辟一條線程的能力。每次需要完成任務(wù)時(shí),從數(shù)組中輪詢獲取隊(duì)列進(jìn)行異步操作。
來看看YYKit源碼是如何實(shí)現(xiàn)一個(gè)線程池的。

準(zhǔn)確來說不是創(chuàng)建線程,是從系統(tǒng)線程池的64條線程中拿到對(duì)應(yīng)個(gè)數(shù)的線程。
