iOS 多線程原理 - 線程與隊(duì)列底層

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

打開libdispatch源碼

  • 獲取主隊(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。

封裝區(qū)分串行/并發(fā)的參數(shù)

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

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

2.初始化隊(duì)列

初始化隊(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ì)列

_dispatch_queue_serial_numbers

串行隊(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__

死鎖崩潰信息回調(diào)函數(shù)

可以清晰看到造成線程死鎖會(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的源碼聲明:

_dq_state_drain_locked_by

_dispatch_lock_is_locked_by的源碼聲明:

_dispatch_lock_is_locked_by

要產(chǎn)生死鎖(這個(gè)函數(shù)返回true)必須是lock_valuetid是相等。

造成線程死鎖的總結(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_sync

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

_dispatch_Block_invoke

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

_dispatch_sync_f的源碼聲明:

_dispatch_sync_f

_dispatch_sync_f_inline的源碼聲明:

_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的源碼聲明:

_dispatch_sync_f_slow

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

_dispatch_sync_function_invoke的源碼聲明:

_dispatch_client_callout的源碼聲明:

_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_async
  • _dispatch_continuation_init保存任務(wù),設(shè)置優(yōu)先級(jí)

_dispatch_continuation_init的源碼聲明:

_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的源碼聲明:

_dispatch_continuation_async

dx_push是宏定義:

#define dx_push(x, y, z) dx_vtable(x)->dq_push(x, y, z)

找到dq_push的聲明:

dq_push

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

_dispatch_lane_concurrent_push的源碼聲明:

_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
_dispatch_lane_create_with_target

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

_dispatch_get_root_queue

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

dq_push

進(jìn)去_dispatch_root_queue_push

_dispatch_root_queue_push

進(jìn)去_dispatch_root_queue_push_inline

_dispatch_root_queue_push_inline

進(jìn)去_dispatch_root_queue_poke

_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_pushdx_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_push

2._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ù)的線程。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容