回顧
在上篇博客已經(jīng)對GCD的柵欄函數(shù)做了一個(gè)基本介紹,還有應(yīng)用的舉例并且對底層源碼進(jìn)行了分析,本篇博客將對信號(hào)量進(jìn)行探索分析!
iOS底層探索之多線程(五)—GCD不同隊(duì)列源碼分析
iOS底層探索之多線程(六)—GCD源碼分析(sync 同步函數(shù)、async 異步函數(shù))
iOS底層探索之多線程(八)—GCD源碼分析(函數(shù)的同步性、異步性、單例)
iOS底層探索之多線程(九)—GCD源碼分析(柵欄函數(shù))
1. 信號(hào)量
1.1 信號(hào)量介紹
信號(hào)量在GCD中是指Dispatch Semaphore,是一種持有計(jì)數(shù)的信號(hào)的東西。有如下三個(gè)方法。
-
dispatch_semaphore_create: 創(chuàng)建信號(hào)量 -
dispatch_semaphore_wait: 信號(hào)量等待 -
dispatch_semaphore_signal: 信號(hào)量釋放
1.2 信號(hào)量舉例
在并發(fā)隊(duì)列里面,可以使用信號(hào)量控制,最大并發(fā)數(shù),如下代碼:
- 信號(hào)量舉例打印結(jié)果

這里一共創(chuàng)建了 4 個(gè)任務(wù),異步并發(fā)執(zhí)行,我在創(chuàng)建信號(hào)量的時(shí)候,設(shè)置了最大并發(fā)數(shù)為2
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t sem = dispatch_semaphore_create(2);
從運(yùn)行的動(dòng)圖,可以看到,每次都是兩個(gè)任務(wù)一起執(zhí)行了,打印的結(jié)果一目了然。
那么再舉個(gè)例子看看,設(shè)置信號(hào)量并發(fā)數(shù)為0
設(shè)置信號(hào)量并發(fā)數(shù)為
0,就相當(dāng)于加鎖的作用,dispatch_semaphore_wait堵住了任務(wù)1讓其等待,等任務(wù) 2執(zhí)行完了,dispatch_semaphore_signal發(fā)送信號(hào),我執(zhí)行完了,你去執(zhí)行吧!
這樣到底信號(hào)量是怎么樣等待,又是怎么樣發(fā)送信號(hào)的呢?
2. 信號(hào)量分析
看看dispatch_semaphore_create的 api的說明
- 當(dāng)兩個(gè)線程需要協(xié)調(diào)特定事件的完成時(shí),為該值傳遞
0很有用。 - 傳遞大于
0的值對于管理有限的資源池很有用,其中池大小等于該值。 - 信號(hào)量的起始值。 傳遞小于
信號(hào)量的起始值。 傳遞小于零的值將導(dǎo)致返回 NULL。的值將導(dǎo)致返回NULL,也就是小于0就不會(huì)正常執(zhí)行。
總結(jié)來說,就是可以控制線程池中的最多并發(fā)數(shù)量
2.1 dispatch_semaphore_signal
dispatch_semaphore_signal
- 在
dispatch_semaphore_signal里面os_atomic_inc2o原子操作自增加1,然后會(huì)判斷,如果value > 0,就會(huì)返回0。 - 例如
value加1之后還是小于0,說明是一個(gè)負(fù)數(shù),也就是調(diào)用dispatch_semaphore_wait次數(shù)太多了,dispatch_semaphore_wait是做減操作的,等會(huì)后面會(huì)分析。 - 加一次后依然小于
0就報(bào)異常"Unbalanced call to dispatch_semaphore_signal(),然后會(huì)調(diào)用_dispatch_semaphore_signal_slow方法的,做容錯(cuò)的處理,_dispatch_sema4_signal是一個(gè)do while循環(huán)
_dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema)
{
_dispatch_sema4_create(&dsema->dsema_sema, _DSEMA4_POLICY_FIFO);
_dispatch_sema4_signal(&dsema->dsema_sema, 1);
return 1;
}
- _dispatch_sema4_signal
void
_dispatch_sema4_signal(_dispatch_sema4_t *sema, long count)
{
do {
int ret = sem_post(sema);
DISPATCH_SEMAPHORE_VERIFY_RET(ret);
} while (--count);
}
2.2 dispatch_semaphore_wait
dispatch_semaphore_wait
dispatch_semaphore_wait源碼如下:
-
os_atomic_dec2o進(jìn)行原子自減1操作,也就是對value值進(jìn)行減操作,控制可并發(fā)數(shù)。 - 如果可并發(fā)數(shù)為
2,則調(diào)用該方法后,變?yōu)?code>1,表示現(xiàn)在并發(fā)數(shù)為1,剩下還可同時(shí)執(zhí)行1個(gè)任務(wù)。如果初始值是0,減操作之后為負(fù)數(shù),則會(huì)調(diào)用_dispatch_semaphore_wait_slow方法。
_dispatch_semaphore_wait_slow方法源碼如下:
_dispatch_semaphore_wait_slow
- 這里對
dispatch_time_t timeout進(jìn)行判斷處理,我們前面的例子里面?zhèn)鞯氖?code>DISPATCH_TIME_FOREVER,那么會(huì)調(diào)用_dispatch_sema4_wait方法
void
_dispatch_sema4_wait(_dispatch_sema4_t *sema)
{
kern_return_t kr;
do {
kr = semaphore_wait(*sema);
} while (kr == KERN_ABORTED);
DISPATCH_SEMAPHORE_VERIFY_KR(kr);
}
_dispatch_sema4_wait方法里面是一個(gè)do-while循環(huán),當(dāng)不滿足條件時(shí),會(huì)一直循環(huán)下去,從而導(dǎo)致流程的阻塞。這也就解釋了上面舉例案里面的執(zhí)行結(jié)果。
上面舉例里面就相當(dāng)于,下圖中的情況
在上圖框框的地方,① 相當(dāng)于②,這里是
do-while循環(huán),所有會(huì)執(zhí)行任務(wù) 2,任務(wù) 1一直在循環(huán)等待。
3. 總結(jié)
-
dispatch_semaphore_wait信號(hào)量等待,內(nèi)部是對并發(fā)數(shù)做自減操作,如果為 小于0,會(huì)執(zhí)行_dispatch_semaphore_wait_slow然后調(diào)用_dispatch_sema4_wait是一個(gè)do-while,知道滿足條件結(jié)束循環(huán) -
dispatch_semaphore_signal信號(hào)量釋放 ,內(nèi)部是對并發(fā)數(shù)做自加操作,直到大于0時(shí),為可操作 - 保持
線程同步,將異步執(zhí)行任務(wù)轉(zhuǎn)換為同步執(zhí)行任務(wù) - 保證
線程安全,為線程加鎖,相當(dāng)于自旋鎖
更多內(nèi)容持續(xù)更新
?? 喜歡就點(diǎn)個(gè)贊吧????
?? 覺得有收獲的,可以來一波,收藏+關(guān)注,評(píng)論 + 轉(zhuǎn)發(fā),以免你下次找不到我????
??歡迎大家留言交流,批評(píng)指正,互相學(xué)習(xí)??,提升自我??