前言:
本系列會(huì)由淺至深的講解iOS Dispatch的用法和實(shí)現(xiàn)原理,從最基本的概念到使用方法碘裕,再到實(shí)現(xiàn)原理攻锰,有時(shí)也會(huì)自己動(dòng)手實(shí)現(xiàn)其中的某些功能英岭。在講到一些基本概念和API介紹時(shí)荆烈,我會(huì)盡量用Apple的官方文檔來(lái)解釋昂儒,我認(rèn)為這樣最為準(zhǔn)確病涨,如果有已經(jīng)看過(guò)Apple文檔的同學(xué)可以跳步這個(gè)部分啰扛,直接看擴(kuò)展部分。
Dispatch Semaphore
單從字面上理解就是信號(hào)量的意思馆揉,他主要的作用在于處理多線程任務(wù)資源訪問(wèn)的問(wèn)題,我有時(shí)候也用于上鎖和解鎖的問(wèn)題抖拦。以下附上Apple的官方解釋:
You can use a dispatch semaphore to regulate the number of tasks allowed to simultaneously access a finite resource. For example, each application is given a limited number of file descriptors to use. If you have a task that processes large numbers of files, you do not want to open so many files at one time that you run out of file descriptors. Instead, you can use a semaphore to limit the number of file descriptors in use at any one time by your file-processing code.
你可以使用dispatch semaphore來(lái)調(diào)節(jié)同時(shí)訪問(wèn)有限資源的任務(wù)個(gè)數(shù)升酣。比如,沒(méi)有程序只能使用有限數(shù)量的文件描述符(fd)态罪。如果你有一個(gè)任務(wù)需要處理大量的文件噩茄,但是你又不想同時(shí)打開(kāi)太多的文件而耗盡fd,這時(shí)你就可以在你的文件處理代碼中用semaphore來(lái)限制使用fd的個(gè)數(shù)复颈。
A dispatch semaphore works like a traditional semaphore, except that when the resource is available, it takes less time to acquire a dispatch semaphore. The reason is that Grand Central Dispatch does not call into the kernel for this particular case. It calls into the kernel only when the resource is not available and the system needs to park your thread until the semaphore is signaled.
Dispatch semaphore和傳統(tǒng)信號(hào)量工作原理類(lèi)似绩聘。但是在資源可用的情況下,使用GCD semaphore將會(huì)消耗較少的時(shí)間耗啦,因?yàn)樵谶@種情況下GCD不會(huì)調(diào)用內(nèi)核凿菩,只有在資源不可用的時(shí)候才會(huì)調(diào)用內(nèi)核,并且系統(tǒng)需要停在你的線程里帜讲,直到發(fā)出這個(gè)信號(hào)衅谷。
GCD Semaphore的API介紹
dispatch_semaphore_create
dispatch_semaphore_t dispatch_semaphore_create(long value);
Creates new counting semaphore with an initial value.
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.
When your application no longer needs the semaphore, it should call dispatch_release to release its reference to the semaphore object and ultimately free its memory.
創(chuàng)建一個(gè)具有初始值的計(jì)數(shù)信號(hào)量。如果你有兩個(gè)線程共同完成某一項(xiàng)工作時(shí)似将,你可以使用一個(gè)初始值為0的semaphore获黔。如果你需要管理一個(gè)有限的資源池時(shí)蚀苛,你使用一個(gè)初始值為資源池的大小的Semaphore。如果你不在使用這個(gè)Semaphore時(shí)玷氏,你應(yīng)該使用dispatch_release銷(xiāo)毀semaphore對(duì)象并且釋放他的內(nèi)存堵未。(在ARC模式下不需要,系統(tǒng)會(huì)自動(dòng)釋放)
Parameters
value:
The starting value for the semaphore. Do not pass a value less than zero.
信號(hào)量的起始值盏触,必須傳一個(gè)大于等于0的值渗蟹,否則返回NULL對(duì)象。
Return
The newly created semaphore, or NULL on failure.
dispatch_semaphore_wait
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
Waits for (decrements) a semaphore.
Decrement the counting semaphore. If the resulting value is less than zero, this function waits for a signal to occur before returning.
等待一個(gè)semaphore耻陕,或者是減少semaphore的計(jì)數(shù)拙徽。每次會(huì)執(zhí)行會(huì)將計(jì)數(shù)器-1.如果減完之后計(jì)數(shù)器小于0的話,會(huì)阻塞在當(dāng)前線程直到接收到信號(hào)诗宣。
Parameters
timeout:When to timeout (see dispatch_time). The constants DISPATCH_TIME_NOW and DISPATCH_TIME_FOREVER are available as a convenience.
設(shè)置超時(shí)膘怕,詳情見(jiàn)dispatch_time。也可以使用常量 DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER召庞。DISPATCH_TIME_NOW可以理解為非阻塞型的等待(就是不等待)岛心。DISPATCH_TIME_FOREVER就是會(huì)一直等待,直到收到信號(hào)篮灼。
Return
Returns zero on success, or non-zero if the timeout occurred.
返回0就是成功忘古,返回非0就是超時(shí)。
dispatch_semaphore_signal
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);
Signals, or increments, a semaphore.
Increment the counting semaphore. If the previous value was less than zero, this function wakes a thread currently waiting in dispatch_semaphore_wait.
發(fā)送信號(hào)诅诱,或者增加一個(gè)信號(hào)量的計(jì)數(shù)髓堪。如果增加之前的值小于0,則將會(huì)喚起一個(gè)在dispatch_semaphore_wait中等待的線程娘荡。(如果有多個(gè)線程在等待干旁,iOS會(huì)根據(jù)線程的優(yōu)先級(jí)來(lái)判斷具體喚醒哪個(gè)線程)。
注意事項(xiàng):
Dispatch Semaphore的用法相對(duì)簡(jiǎn)單炮沐,只要注一點(diǎn)就可以了争群。就是如果semaphore正在wait狀態(tài),也就是正在執(zhí)行dispatch_semaphore_wait操作大年,這時(shí)釋放Semaphore的內(nèi)存會(huì)程序會(huì)Crash换薄。
GCD Semaphore 的使用
GCD Semaphore非常簡(jiǎn)單,很好理解也很好上手翔试。
案例 多線程處理文件描述符:
假如我們?cè)谠O(shè)計(jì)socket通信模塊或者是文件系統(tǒng)轻要,為了防止數(shù)據(jù)錯(cuò)亂,我們不允許多線程同時(shí)往一個(gè)socket或者一個(gè)文件里面寫(xiě)數(shù)據(jù)垦缅。所以我們使用Semaphore添加限制伦腐。
- (void)sendData:(dispatch_data_t)data overPipeline:(dispatch_io_t)pipeline callback:(void (^)(NSError *))callback
{
if(!_semaphore){
// 初始化信號(hào)量,
_semaphore = dispatch_semaphore_create(1);
}
// 等待信號(hào)量
dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
dispatch_io_write(pipeline, 0, data, _writeQueue, ^(bool done, dispatch_data_t data, int _errno){
// 寫(xiě)完了失都,發(fā)送信號(hào)讓下一個(gè)等待的線程進(jìn)行寫(xiě)的操作柏蘑。
dispatch_semaphore_signal(_semaphore);
if (done && callback){
callback(_errno == 0 ? nil : [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:_errno userInfo:nil]);
}
});
}