想象種場景:1.要下載十個文件舵变,但是同一時刻只想讓兩個文件處于下載狀態(tài),用
GCD如何實現(xiàn)?
這種問題涉及到對并發(fā)的控制瘦穆,使用信號量就可以很好的控制纪隙。
信號量是一個整形值并且具有一個初始計數(shù)值,并且支持兩個操作:信號通知和等待扛或。當(dāng)一個信號量被信號通知绵咱,其計數(shù)會被增加。當(dāng)一個線程在一個信號量上等待時熙兔,線程會被阻塞(如果有必要的話)悲伶,直至計數(shù)器大于零艾恼,然后線程會減少這個計數(shù)。
在GCD中有三個函數(shù)是關(guān)于semaphore的操作麸锉,分別是:
dispatch_semaphore_create 創(chuàng)建一個semaphore
dispatch_semaphore_signal 發(fā)送一個信號,使信號量+1
dispatch_semaphore_wait 等待信號
不要看他們的樣子很生疏钠绍,這個semaphore單詞還不認(rèn)識,感覺很晦澀花沉,其實真正用的時候就會發(fā)現(xiàn)還是很簡單的柳爽。這套API簡單到就3個函數(shù),函數(shù)的參數(shù)一般也就一兩個主穗。
信號量可以拿停車場的例子來類比泻拦,方便理解。
假如有一個停車場有3個空車位忽媒,停車場外面有個信號燈顯示著有3個空位争拐,這時停車場外面來了五輛車,前三量車依次進(jìn)入停車場晦雨,每進(jìn)入一輛車架曹,信號燈都要減1,當(dāng)信號燈減成0的時候就意味著沒有車位了闹瞧,外面的車只能等待绑雄,不得進(jìn)入停車場。 而當(dāng)一輛車開走的時候奥邮,信號燈上又會加1万牺,意味著有空車位了,就可以有一輛車進(jìn)入洽腺。dispatch_queue_create就是創(chuàng)建信號脚粟,dispatch_semaphore_wait就是在停車場外鳴笛,準(zhǔn)備進(jìn)入停車場蘸朋,此時如果有空余車外邑茄,可以順利進(jìn)入胰丁,并讓信號燈減1,加入沒有車位缘回,就一直在外面等車秕硝,直到信號燈數(shù)量大于1時候就馬上進(jìn)入 dispatch_semaphore_signal就是讓信號燈數(shù)量+1,如果外面有等待的車輛次哈, 就告訴它可以進(jìn)來了莹规。
一 .API介紹
1.dispatch_semaphore_create
/*!
* @function dispatch_semaphore_create
*
* @abstract
* Creates new counting semaphore with an initial value.
* 使用初始化值創(chuàng)建一個新的計數(shù)信號量
* @discussion
* Passing zero for the value is useful for when two threads need to reconcile
* the completion of a particular event. Passing a value greater than zero is
* useful for managing a finite pool of resources, where the pool size is equal
* to the value.
* 傳0用于 兩個線程需要以特定順序完成某個事件请梢,傳大于0的數(shù)用于管理資源,資源池的資源數(shù)要和傳入的值一致
* @param value
* The starting value for the semaphore. Passing a value less than zero will
* cause NULL to be returned.
* 信號量初始值。傳入低于0的值會返回NULL
* @result
* The newly created semaphore, or NULL on failure.
*/
dispatch_semaphore_t
dispatch_semaphore_create(long value);
2.dispatch_semaphore_wait
/*!
* @function dispatch_semaphore_wait
*
* @abstract
* Wait (decrement) for a semaphore.
* 等待(降低)信號量
* @discussion
* Decrement the counting semaphore. If the resulting value is less than zero,
* this function waits for a signal to occur before returning.
* 降低信號量辐马。如果返回值小于0佑惠,該函數(shù)會阻塞 等待直到信號發(fā)生才能返回
* @param dsema
* The semaphore. The result of passing NULL in this parameter is undefined.
* 信號對象。傳NULL會發(fā)生不確定結(jié)果
* @param timeout
* When to timeout (see dispatch_time). As a convenience, there are the
* DISPATCH_TIME_NOW and DISPATCH_TIME_FOREVER constants.
* 超時時間齐疙。為了方便膜楷,系統(tǒng)提供了兩個常亮:DISPATCH_TIME_NOW和DISPATCH_TIME_FOREVER
* @result
* Returns zero on success, or non-zero if the timeout occurred.
* 返回0代表成功,返回非0代表超時時間到達(dá)了
*/
long
dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
3.dispatch_semaphore_signal
/*!
* @function dispatch_semaphore_signal
*
* @abstract
* Signal (increment) a semaphore.
* 發(fā)送(增加)一個信號量
* @discussion
* Increment the counting semaphore. If the previous value was less than zero,
* this function wakes a waiting thread before returning.
* 這個方法喚醒一個等待的線程 然后返回
* @param dsema The counting semaphore.
* The result of passing NULL in this parameter is undefined.
* 傳0是未定義的
* @result
* This function returns non-zero if a thread is woken. Otherwise, zero is
* returned.
* 返回非0代表有線程被喚醒贞奋。否則赌厅,返回0
*/
long
dispatch_semaphore_signal(dispatch_semaphore_t dsema);
二.應(yīng)用
應(yīng)用也很簡單了
//創(chuàng)建信號量 初始化為0
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
_semaphore = semaphore;
for (int i = 0; i < 10; i ++ ) {
dispatch_async(globalqueue, ^{
//執(zhí)行任務(wù)會使信號量減1,當(dāng)執(zhí)行兩個任務(wù)后轿塔,信號量變?yōu)?特愿,無法在繼續(xù)執(zhí)行。
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
sleep(3);
NSLog(@"---------任務(wù)%d完成---%@",i,[NSThread currentThread]);
//執(zhí)行完任務(wù)后勾缭,使信號量加1揍障,繼續(xù)執(zhí)行后面任務(wù)
dispatch_semaphore_signal(semaphore);
});
}
使用for循環(huán)將十個任務(wù)添加到并行隊列中,正常情況下俩由,十個任務(wù)會同時執(zhí)行毒嫡。使用dispatch_semaphore就可以控制同一時刻只能有兩個任務(wù)在執(zhí)行。
dispatch_semaphore也可以控制兩個任務(wù)的執(zhí)行順序幻梯,比如任務(wù)1 和任務(wù)2 兜畸,創(chuàng)建一個出事信號量為0的semaphore對象,在任務(wù)1前面使用dispatch_semaphore_wait函數(shù)阻塞碘梢,等待信號量大于0 當(dāng)任務(wù)2完成的時候發(fā)送信號咬摇,使任務(wù)1執(zhí)行。 這樣就可以控制任務(wù)2先完成,任務(wù)1后完成煞躬。