首先,我理解的dispatch_semaphore
有兩個(gè)主要應(yīng)用 :
1. 保持線程同步
2. 為線程加鎖
先看下相關(guān)的3個(gè)方法:
-
dispatch_semaphore_t dispatch_semaphore_create(long value)
:方法接收一個(gè)long類型的參數(shù), 返回一個(gè)dispatch_semaphore_t
類型的信號(hào)量住册,值為
這個(gè)參數(shù)一定要是大于等于0的
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)```:接收一個(gè)信號(hào)和時(shí)間值,若信號(hào)的信號(hào)量為0且预,則會(huì)阻塞當(dāng)前線程槽袄,直到信號(hào)量大于0或者經(jīng)過(guò)輸入的時(shí)間值;若信號(hào)量大于0锋谐,則會(huì)使信號(hào)量減1并返回遍尺,程序繼續(xù)住下執(zhí)行
-
long dispatch_semaphore_signal(dispatch_semaphore_t dsema)
:使信號(hào)量加1并返回
介紹完了相關(guān)方法,下面開(kāi)始介紹兩種應(yīng)用
保持線程同步
先看代碼
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
__block int j = 0;
dispatch_async(queue, ^{
j = 100;
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"finish j = %zd", j);
結(jié)果輸出 j = 100怀估;
如果注掉dispatch_semaphore_wait
這一行狮鸭,則 j = 0;
注釋: 由于是將block
異步添加到一個(gè)并行隊(duì)列里面多搀,所以程序在主線程躍過(guò)block直接到dispatch_semaphore_wait
這一行歧蕉,因?yàn)?code>semaphore信號(hào)量為0,時(shí)間值為DISPATCH_TIME_FOREVER
康铭,所以當(dāng)前線程會(huì)一直阻塞惯退,直到block在子線程執(zhí)行到dispatch_semaphore_signal
,使信號(hào)量+1从藤,此時(shí)semaphore
信號(hào)量為1了催跪,所以程序繼續(xù)往下執(zhí)行。這就保證了線程間同步了夷野。
為線程加鎖
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = 0; i <100; i++) {
dispatch_async(queue, ^{
// 相當(dāng)于加鎖
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"i = %zd semaphore = %@", i, semaphore);
// 相當(dāng)于解鎖
dispatch_semaphore_signal(semaphore);
});
}
注釋:當(dāng)線程1執(zhí)行到dispatch_semaphore_wait
這一行時(shí)懊蒸,semaphore
的信號(hào)量為1,所以使信號(hào)量-1變?yōu)?悯搔,并且線程1繼續(xù)往下執(zhí)行骑丸;如果當(dāng)在線程1NSLog這一行代碼還沒(méi)執(zhí)行完的時(shí)候,又有線程2來(lái)訪問(wèn)妒貌,執(zhí)行dispatch_semaphore_wait
時(shí)由于此時(shí)信號(hào)量為0通危,且時(shí)間為DISPATCH_TIME_FOREVER
,所以會(huì)一直阻塞線程2(此時(shí)線程2處于等待狀態(tài)),直到線程1執(zhí)行完NSLog并執(zhí)行完dispatch_semaphore_signal
使信號(hào)量為1后灌曙,線程2才能解除阻塞繼續(xù)住下執(zhí)行菊碟。以上可以保證同時(shí)只有一個(gè)線程執(zhí)行NSLog這一行代碼。
簡(jiǎn)單來(lái)講 信號(hào)量為0則阻塞線程在刺,大于0則不會(huì)阻塞逆害。則我們通過(guò)改變信號(hào)量的值,來(lái)控制是否阻塞線程蚣驼,從而達(dá)到線程同步.
下面我們逐一介紹三個(gè)函數(shù):
(1)dispatch_semaphore_create
的聲明為:
dispatch_semaphore_t dispatch_semaphore_create(long value);
傳入的參數(shù)為long
忍燥,輸出一個(gè)dispatch_semaphore_t
類型且值為value
的信號(hào)量。
值得注意的是隙姿,這里的傳入的參數(shù)value
必須大于或等于0
,否則dispatch_semaphore_create
會(huì)返回NULL
厂捞。
(關(guān)于信號(hào)量输玷,我就不在這里累述了队丝,網(wǎng)上很多介紹這個(gè)的。我們這里主要講一下dispatch_semaphore
這三個(gè)函數(shù)的用法)欲鹏。
(2)dispatch_semaphore_signal
的聲明為:
long dispatch_semaphore_signal(dispatch_semaphore_t dsema)
這個(gè)函數(shù)會(huì)使傳入的信號(hào)量dsema
的值加1
机久;(至于返回值,待會(huì)兒再講)
(3) dispatch_semaphore_wait
的聲明為:
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)赔嚎;
這個(gè)函數(shù)會(huì)使傳入的信號(hào)量dsema
的值減1
膘盖;
這個(gè)函數(shù)的作用是這樣的,如果dsema
信號(hào)量的值大于0
尤误,該函數(shù)所處線程就繼續(xù)執(zhí)行下面的語(yǔ)句侠畔,并且將信號(hào)量的值減1
;
如果desema
的值為0
损晤,那么這個(gè)函數(shù)就阻塞當(dāng)前線程等待timeout
(注意timeout
的類型為dispatch_time_t
软棺,
不能直接傳入整形或float
型數(shù)),如果等待的期間desema
的值被dispatch_semaphore_signal
函數(shù)加1
了尤勋,
且該函數(shù)(即dispatch_semaphore_wait
)所處線程獲得了信號(hào)量喘落,那么就繼續(xù)向下執(zhí)行并將信號(hào)量減1
。
如果等待期間沒(méi)有獲取到信號(hào)量或者信號(hào)量的值一直為0
最冰,那么等到timeout
時(shí)瘦棋,其所處線程自動(dòng)執(zhí)行其后語(yǔ)句。
(4)dispatch_semaphore_signal
的返回值為long
類型暖哨,當(dāng)返回值為0
時(shí)表示當(dāng)前并沒(méi)有線程等待其處理的信號(hào)量赌朋,其處理
的信號(hào)量的值加1
即可。當(dāng)返回值不為0
時(shí)鹿蜀,表示其當(dāng)前有(一個(gè)或多個(gè))線程等待其處理的信號(hào)量箕慧,并且該函數(shù)喚醒了一
個(gè)等待的線程(當(dāng)線程有優(yōu)先級(jí)時(shí),喚醒優(yōu)先級(jí)最高的線程茴恰;否則隨機(jī)喚醒)颠焦。
dispatch_semaphore_wait
的返回值也為long
型。當(dāng)其返回0
時(shí)表示在timeout
之前往枣,該函數(shù)所處的線程被成功喚醒伐庭。
當(dāng)其返回不為0
時(shí),表示timeout
發(fā)生分冈。
隊(duì)列加鎖
// 創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
// 創(chuàng)建信號(hào)量圾另,并且設(shè)置值為10
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 100; i++)
{ // 由于是異步執(zhí)行的,所以每次循環(huán)Block里面的dispatch_semaphore_signal根本還沒(méi)有執(zhí)行就會(huì)執(zhí)行dispatch_semaphore_wait雕沉,從而semaphore-1.當(dāng)循環(huán)10此后集乔,semaphore等于0,則會(huì)阻塞線程坡椒,直到執(zhí)行了Block的dispatch_semaphore_signal 才會(huì)繼續(xù)執(zhí)行
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_group_async(group, queue, ^{
NSLog(@"%i",i);
sleep(2);
// 每次發(fā)送信號(hào)則semaphore會(huì)+1扰路,
dispatch_semaphore_signal(semaphore);
});
}