iOS多線程之并發(fā)編程-3

Dispatch Sources

現(xiàn)代系統(tǒng)通常提供異步接口扬绪,允許應(yīng)用向系統(tǒng)提交請求太伊,然后在系統(tǒng)處理請求時應(yīng)用可以繼續(xù)處理自己的事情狞谱。Grand Central Dispatch正是基于這個基本行為而設(shè)計,允許你提交請求冶伞,并通過block和dispatch queue報告結(jié)果系任。

dispatch source是基礎(chǔ)數(shù)據(jù)類型恳蹲,協(xié)調(diào)特定底層系統(tǒng)事件的處理。Grand Central Dispatch支持以下dispatch source:

Timer dispatch source:定期產(chǎn)生通知

Signal dispatch source:UNIX信號到達(dá)時產(chǎn)生通知

Descriptor dispatch source:各種文件和socket操作的通知

數(shù)據(jù)可讀

數(shù)據(jù)可寫

文件在文件系統(tǒng)中被刪除俩滥、移動嘉蕾、重命名

文件元數(shù)據(jù)信息改變

Process dispatch source:進(jìn)程相關(guān)的事件通知

當(dāng)進(jìn)程退出時

當(dāng)進(jìn)程發(fā)起fork或exec等調(diào)用

信號被遞送到進(jìn)程

Mach port dispatch source:Mach相關(guān)事件的通知

Custom dispatch source:你自己定義并自己觸發(fā)

Dispatch source替代了異步回調(diào)函數(shù),來處理系統(tǒng)相關(guān)的事件霜旧。當(dāng)你配置一個dispatch source時错忱,你指定要監(jiān)測的事件、dispatch queue挂据、以及處理事件的代碼(block或函數(shù))以清。當(dāng)事件發(fā)生時,dispatch source會提交你的block或函數(shù)到指定的queue去執(zhí)行

和手工提交到queue的任務(wù)不同崎逃,dispatch source為應(yīng)用提供連續(xù)的事件源掷倔。除非你顯式地取消,dispatch source會一直保留與dispatch queue的關(guān)聯(lián)个绍。只要相應(yīng)的事件發(fā)生勒葱,就會提交關(guān)聯(lián)的代碼到dispatch queue去執(zhí)行。

為了防止事件積壓到dispatch queue巴柿,dispatch source實現(xiàn)了事件合并機(jī)制凛虽。如果新事件在上一個事件處理器出列并執(zhí)行之前到達(dá),dispatch source會將新舊事件的數(shù)據(jù)合并广恢。根據(jù)事件類型的不同凯旋,合并操作可能會替換舊事件,或者更新舊事件的信息钉迷。

創(chuàng)建Dispatch Source

創(chuàng)建dispatch source需要同時創(chuàng)建事件源和dispatch source本身至非。事件源是處理事件所需要的native數(shù)據(jù)結(jié)構(gòu),例如基于描述符的dispatch source糠聪,你需要打開描述符;基于進(jìn)程的事件睡蟋,你需要獲得目標(biāo)程序的進(jìn)程ID。

然后可以如下創(chuàng)建相應(yīng)的dispatch source:

使用 dispatch_source_create 函數(shù)創(chuàng)建dispatch source

配置dispatch source:

為dispatch source設(shè)置一個事件處理器

對于定時器源枷颊,使用 dispatch_source_set_timer 函數(shù)設(shè)置定時器信息

為dispatch source賦予一個取消處理器(可選)調(diào)用 dispatch_resume 函數(shù)開始處理事件由于dispatch source必須進(jìn)行額外的配置才能被使用戳杀,dispatch_source_create 函數(shù)返回的dispatch source將處于掛起狀態(tài)。此時dispatch source會接收事件夭苗,但是不會進(jìn)行處理信卡。這時候你可以安裝事件處理器,并執(zhí)行額外的配置题造。

編寫和安裝一個事件處理器

你需要定義一個事件處理器來處理事件傍菇,可以是函數(shù)或block對象,并使用 dispatch_source_set_event_handler 或 dispatch_source_set_event_handler_f 安裝事件處理器界赔。事件到達(dá)時丢习,dispatch source會提交你的事件處理器到指定的dispatch queue牵触,由queue執(zhí)行事件處理器。

事件處理器的代碼負(fù)責(zé)處理所有到達(dá)的事件咐低。如果事件處理器已經(jīng)在queue中并等待處理已經(jīng)到達(dá)的事件揽思,如果此時又來了一個新事件,dispatch source會合并這兩個事件见擦。事件處理器通常只能看到最新事件的信息钉汗,不過某些類型的dispatch source也能獲得已經(jīng)發(fā)生以及合并的事件信息。

如果事件處理器已經(jīng)開始執(zhí)行鲤屡,一個或多個新事件到達(dá)损痰,dispatch source會保留這些事件,直到前面的事件處理器完成執(zhí)行酒来。然后以新事件再次提交處理器到queue卢未。

函數(shù)事件處理器有一個context指針指向dispatch source對象,沒有返回值堰汉。Block事件處理器沒有參數(shù)尝丐,也沒有返回值。

// Block-based event handler

void (^dispatch_block_t)(void)

// Function-based event handler

void (*dispatch_function_t)(void *)

在事件處理器中衡奥,你可以從dispatch source中獲得事件的信息爹袁,函數(shù)處理器可以直接使用參數(shù)指針,Block則必須自己捕獲到dispatch source指針矮固,一般block定義時會自動捕獲到外部定義的所有變量失息。

dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,

myDescriptor, 0, myQueue);

dispatch_source_set_event_handler(source, ^{

// Get some data from the source variable, which is captured

// from the parent context.

size_t estimated = dispatch_source_get_data(source);

// Continue reading the descriptor...

});

dispatch_resume(source);

Block捕獲外部變量允許更大的靈活性和動態(tài)性。當(dāng)然档址,在Block中這些變量默認(rèn)是只讀的盹兢,雖然可以使用__block來修改捕獲的變量,但是你最好不要在事件處理器中這樣做守伸。因為Dispatch source異步執(zhí)行事件處理器绎秒,當(dāng)事件處理器修改原始外部變量時,有可能這些變量已經(jīng)不存在了尼摹。

下面是事件處理器能夠獲得的事件信息:

函數(shù) 描述

dispatch_source_get_handle 這個函數(shù)返回dispatch source管理的底層系統(tǒng)數(shù)據(jù)類型见芹。

對于描述符dispatch source,函數(shù)返回一個int蠢涝,表示關(guān)聯(lián)的描述符

對于信號dispatch source玄呛,函數(shù)返回一個int,表示最新事件的信號數(shù)值

對于進(jìn)程dispatch source和二,函數(shù)返回一個pid_t數(shù)據(jù)結(jié)構(gòu)徘铝,表示被監(jiān)控的進(jìn)程

對于Mach port dispatch source,函數(shù)返回一個 mach_port_t 數(shù)據(jù)結(jié)構(gòu)

對于其它dispatch source,函數(shù)返回的值未定義

dispatch_source_get_data 這個函數(shù)返回事件關(guān)聯(lián)的所有未決數(shù)據(jù)惕它。

對于從文件中讀取數(shù)據(jù)的描述符dispatch source怕午,這個函數(shù)返回可以讀取的字節(jié)數(shù)

對于向文件中寫入數(shù)據(jù)的描述符dispatch source,如果可以寫入淹魄,則返回正數(shù)值

對于監(jiān)控文件系統(tǒng)活動的描述符dispatch source郁惜,函數(shù)返回一個常量,表示發(fā)生的事件類型揭北,參考 dispatch_source_vnode_flags_t 枚舉類型

對于進(jìn)程dispatch source,函數(shù)返回一個常量吏颖,表示發(fā)生的事件類型搔体,參考 dispatch_source_proc_flags_t 枚舉類型

對于Mach port dispatch source,函數(shù)返回一個常量半醉,表示發(fā)生的事件類型疚俱,參考 dispatch_source_machport_flags_t 枚舉類型

對于自定義dispatch source,函數(shù)返回從現(xiàn)有數(shù)據(jù)創(chuàng)建的新數(shù)據(jù)缩多,以及傳遞給 dispatch_source_merge_data 函數(shù)的新數(shù)據(jù)呆奕。

dispatch_source_get_mask 這個函數(shù)返回用來創(chuàng)建dispatch source的事件標(biāo)志

對于進(jìn)程dispatch source,函數(shù)返回dispatch source接收到的事件掩碼衬吆,參考 dispatch_source_proc_flags_t 枚舉類型

對于發(fā)送權(quán)利的Mach port dispatch source梁钾,函數(shù)返回期望事件的掩碼,參考 dispatch_source_mach_send_flags_t 枚舉類型

對于自定義 “或” 的dispatch source逊抡,函數(shù)返回用來合并數(shù)據(jù)值的掩碼姆泻。

安裝一個取消處理器

取消處理器在dispatch soruce釋放之前執(zhí)行清理工作。多數(shù)類型的dispatch source不需要取消處理器冒嫡,除非你對dispatch source有自定義行為需要在釋放時執(zhí)行拇勃。但是使用描述符或Mach port的dispatch source必須設(shè)置取消處理器,用來關(guān)閉描述符或釋放Mach port孝凌。否則可能導(dǎo)致微妙的bug方咆,這些結(jié)構(gòu)體會被系統(tǒng)其它部分或你的應(yīng)用在不經(jīng)意間重用。

你可以在任何時候安裝取消處理器蟀架,但通常我們在創(chuàng)建dispatch source時就會安裝取消處理器瓣赂。使用 dispatch_source_set_cancel_handler 或 dispatch_source_set_cancel_handler_f 函數(shù)來設(shè)置取消處理器。

下面取消處理器關(guān)閉描述符:

dispatch_source_set_cancel_handler(mySource, ^{

close(fd); // Close a file descriptor opened earlier.

});

修改目標(biāo)Queue

在創(chuàng)建dispatch source時可以指定一個queue片拍,用來執(zhí)行事件處理器和取消處理器钩述。不過你也可以使用 dispatch_set_target_queue 函數(shù)在任何時候修改目標(biāo)queue。修改queue可以改變執(zhí)行dispatch source事件的優(yōu)先級穆碎。

修改dispatch source的目標(biāo)queue是異步操作牙勘,dispatch source會盡可能快地完成這個修改。如果事件處理器已經(jīng)進(jìn)入queue并等待處理,它會繼續(xù)在原來的Queue中執(zhí)行方面。隨后到達(dá)的所有事件的處理器都會在后面修改的queue中執(zhí)行放钦。

關(guān)聯(lián)自定義數(shù)據(jù)到dispatch source

和Grand Central Dispatch的其它類型一樣,你可以使用 dispatch_set_context 函數(shù)關(guān)聯(lián)自定義數(shù)據(jù)到dispatch source恭金。使用context指針存儲事件處理器需要的任何數(shù)據(jù)操禀。如果你在context指針中存儲了數(shù)據(jù),你就應(yīng)該安裝一個取消處理器横腿,在dispatch source不再需要時釋放這些context自定義數(shù)據(jù)颓屑。

如果你使用block實現(xiàn)事件處理器,你也可以捕獲本地變量耿焊,并在Block中使用揪惦。雖然這樣也可以代替context指針,但是你應(yīng)該明智地使用Block捕獲變量罗侯。因為dispatch source長時間存在于應(yīng)用中器腋,Block捕獲指針變量時必須非常小心,因為指針指向的數(shù)據(jù)可能會被釋放钩杰,因此需要復(fù)制數(shù)據(jù)或retain纫塌。不管使用哪種方法,你都應(yīng)該提供一個取消處理器讲弄,在最后釋放這些數(shù)據(jù)措左。

Dispatch Source的內(nèi)存管理

Dispatch Source也是引用計數(shù)的數(shù)據(jù)類型,初始計數(shù)為1避除,可以使用 dispatch_retain 和 dispatch_release 函數(shù)來增加和減少引用計數(shù)媳荒。引用計數(shù)到達(dá)0時,系統(tǒng)自動釋放dispatch source數(shù)據(jù)結(jié)構(gòu)驹饺。

dispatch source的所有權(quán)可以由dispatch source內(nèi)部或外部進(jìn)行管理钳枕。外部所有權(quán)時,另一個對象擁有dispatch source赏壹,并負(fù)責(zé)在不需要時釋放它鱼炒。內(nèi)部所有權(quán)時,dispatch source自己擁有自己蝌借,并負(fù)責(zé)在適當(dāng)?shù)臅r候釋放自己昔瞧。雖然外部所有權(quán)很常用,當(dāng)你希望創(chuàng)建自主dispatch source菩佑,并讓它自己管理自己的行為時自晰,可以使用內(nèi)部所有權(quán)。例如dispatch source應(yīng)用單一全局事件時稍坯,可以讓它自己處理該事件酬荞,并立即退出搓劫。

Dispatch Source示例

創(chuàng)建一個定時器

定時器dispatch source定時產(chǎn)生事件,可以用來發(fā)起定時執(zhí)行的任務(wù)混巧,如游戲或其它圖形應(yīng)用枪向,可以使用定時器來更新屏幕或動畫。你也可以設(shè)置定時器咧党,并在固定間隔事件中檢查服務(wù)器的新信息秘蛔。

所有定時器dispatch source都是間隔定時器,一旦創(chuàng)建傍衡,會按你指定的間隔定期遞送事件深员。你需要為定時器dispatch source指定一個期望的定時器事件精度,也就是leeway值蛙埂,讓系統(tǒng)能夠靈活地管理電源并喚醒內(nèi)核倦畅。例如系統(tǒng)可以使用leeway值來提前或延遲觸發(fā)定時器,使其更好地與其它系統(tǒng)事件結(jié)合箱残。創(chuàng)建自己的定時器時滔迈,你應(yīng)該盡量指定一個leeway值止吁。

就算你指定leeway值為0被辑,也不要期望定時器能夠按照精確的納秒來觸發(fā)事件。系統(tǒng)會盡可能地滿足你的需求敬惦,但是無法保證完全精確的觸發(fā)時間盼理。

當(dāng)計算機(jī)睡眠時,定時器dispatch source會被掛起俄删,稍后系統(tǒng)喚醒時宏怔,定時器dispatch source也會自動喚醒。根據(jù)你提供的配置畴椰,暫停定時器可能會影響定時器下一次的觸發(fā)臊诊。如果定時器dispatch source使用 dispatch_time 函數(shù)或 DISPATCH_TIME_NOW 常量設(shè)置,定時器dispatch source會使用系統(tǒng)默認(rèn)時鐘來確定何時觸發(fā)斜脂,但是默認(rèn)時鐘在計算機(jī)睡眠時不會繼續(xù)抓艳。

如果你使用 dispatch_walltime 函數(shù)來設(shè)置定時器dispatch source,則定時器會根據(jù)掛鐘時間來跟蹤帚戳,這種定時器比較適合觸發(fā)間隔相對比較大的場合玷或,可以防止定時器觸發(fā)間隔出現(xiàn)太大的誤差。

下面是定時器dispatch source的一個例子片任,每30秒觸發(fā)一次偏友,leeway值為1,因為間隔相對較大对供,使用 dispatch_walltime 來創(chuàng)建定時器位他。定時器會立即觸發(fā)第一次,隨后每30秒觸發(fā)一次。 MyPeriodicTask 和 MyStoreTimer 是自定義函數(shù)棱诱,用于實現(xiàn)定時器的行為泼橘,并存儲定時器到應(yīng)用的數(shù)據(jù)結(jié)構(gòu)。

dispatch_source_t CreateDispatchTimer(uint64_t interval,

uint64_t leeway,

dispatch_queue_t queue,

dispatch_block_t block)

{

dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,

0, 0, queue);

if (timer)

{

dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);

dispatch_source_set_event_handler(timer, block);

dispatch_resume(timer);

}

return timer;

}

void MyCreateTimer()

{

dispatch_source_t aTimer = CreateDispatchTimer(30ull * NSEC_PER_SEC,

1ull * NSEC_PER_SEC,

dispatch_get_main_queue(),

^{ MyPeriodicTask(); });

// Store it somewhere for later use.

if (aTimer)

{

MyStoreTimer(aTimer);

}

}

雖然定時器dispatch source是接收時間事件的主要方法迈勋,你還可以使用其它選擇炬灭。如果想在指定時間間隔后執(zhí)行一個block,可以使用 dispatch_after 或 dispatch_after_f 函數(shù)靡菇。這兩個函數(shù)非常類似于dispatch_async重归,但是只允許你指定一個時間值,時間一到就自動提交block到queue中執(zhí)行厦凤,時間值可以指定為相對或絕對時間鼻吮。

從描述符中讀取數(shù)據(jù)

要從文件或socket中讀取數(shù)據(jù),需要打開文件或socket较鼓,并創(chuàng)建一個 DISPATCH_SOURCE_TYPE_READ 類型的dispatch source椎木。你指定的事件處理器必須能夠讀取和處理描述符中的內(nèi)容。對于文件博烂,需要讀取文件數(shù)據(jù)香椎,并為應(yīng)用創(chuàng)建適當(dāng)?shù)臄?shù)據(jù)結(jié)構(gòu);對于網(wǎng)絡(luò)socket,需要處理最新接收到的網(wǎng)絡(luò)數(shù)據(jù)禽篱。

讀取數(shù)據(jù)時畜伐,你總是應(yīng)該配置描述符使用非阻塞操作,雖然你可以使用 dispatch_source_get_data 函數(shù)查看當(dāng)前有多少數(shù)據(jù)可讀躺率,但在你調(diào)用它和實際讀取數(shù)據(jù)之間玛界,可用的數(shù)據(jù)數(shù)量可能會發(fā)生變化。如果底層文件被截斷悼吱,或發(fā)生網(wǎng)絡(luò)錯誤慎框,從描述符中讀取會阻塞當(dāng)前線程,停止在事件處理器中間并阻止dispatch queue去執(zhí)行其它任務(wù)后添。對于串行queue笨枯,這樣還可能會死鎖,即使是并發(fā)queue吕朵,也會減少queue能夠執(zhí)行的任務(wù)數(shù)量猎醇。

下面例子配置dispatch source從文件中讀取數(shù)據(jù),事件處理器讀取指定文件的全部內(nèi)容到緩沖區(qū)努溃,并調(diào)用一個自定義函數(shù)來處理這些數(shù)據(jù)硫嘶。調(diào)用方可以使用返回的dispatch source在讀取操作完成之后,來取消這個事件梧税。為了確保dispatch queue不會阻塞沦疾,這里使用了fcntl函數(shù)称近,配置文件描述符執(zhí)行非阻塞操作。dispatch source安裝了取消處理器哮塞,確保最后關(guān)閉了文件描述符刨秆。

dispatch_source_t ProcessContentsOfFile(const char* filename)

{

// Prepare the file for reading.

int fd = open(filename, O_RDONLY);

if (fd == -1)

return NULL;

fcntl(fd, F_SETFL, O_NONBLOCK);? // Avoid blocking the read operation

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_source_t readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,

fd, 0, queue);

if (!readSource)

{

close(fd);

return NULL;

}

// Install the event handler

dispatch_source_set_event_handler(readSource, ^{

size_t estimated = dispatch_source_get_data(readSource) + 1;

// Read the data into a text buffer.

char* buffer = (char*)malloc(estimated);

if (buffer)

{

ssize_t actual = read(fd, buffer, (estimated));

Boolean done = MyProcessFileData(buffer, actual);? // Process the data.

// Release the buffer when done.

free(buffer);

// If there is no more data, cancel the source.

if (done)

dispatch_source_cancel(readSource);

}

});

// Install the cancellation handler

dispatch_source_set_cancel_handler(readSource, ^{close(fd);});

// Start reading the file.

dispatch_resume(readSource);

return readSource;

}

在這個例子中,自定義的 MyProcessFileData 函數(shù)確定讀取到足夠的數(shù)據(jù)忆畅,返回YES告訴dispatch source讀取已經(jīng)完成衡未,可以取消任務(wù)。通常讀取描述符的dispatch source在還有數(shù)據(jù)可讀時家凯,會重復(fù)調(diào)度事件處理器缓醋。如果socket連接關(guān)閉或到達(dá)文件末尾,dispatch source自動停止調(diào)度事件處理器绊诲。如果你自己確定不再需要dispatch source送粱,也可以手動取消它。

向描述符寫入數(shù)據(jù)

向文件或socket寫入數(shù)據(jù)非常類似于讀取數(shù)據(jù)掂之,配置描述符為寫入操作后抗俄,創(chuàng)建一個 DISPATCH_SOURCE_TYPE_WRITE 類型的dispatch source,創(chuàng)建好之后世舰,系統(tǒng)會調(diào)用事件處理器动雹,讓它開始向文件或socket寫入數(shù)據(jù)。當(dāng)你完成寫入后冯乘,使用 dispatch_source_cancel 函數(shù)取消dispatch source洽胶。

寫入數(shù)據(jù)也應(yīng)該配置文件描述符使用非阻塞操作晒夹,雖然 dispatch_source_get_data 函數(shù)可以查看當(dāng)前有多少可用寫入空間裆馒,但這個值只是建議性的,而且在你執(zhí)行寫入操作時可能會發(fā)生變化丐怯。如果發(fā)生錯誤喷好,寫入數(shù)據(jù)到阻塞描述符,也會使事件處理器停止在執(zhí)行中途读跷,并阻止dispatch queue執(zhí)行其它任務(wù)梗搅。串行queue會產(chǎn)生死鎖,并發(fā)queue則會減少能夠執(zhí)行的任務(wù)數(shù)量效览。

下面是使用dispatch source寫入數(shù)據(jù)到文件的例子无切,創(chuàng)建文件后,函數(shù)傳遞文件描述符到事件處理器丐枉。MyGetData函數(shù)負(fù)責(zé)提供要寫入的數(shù)據(jù)哆键,在數(shù)據(jù)寫入到文件之后,事件處理器取消dispatch source瘦锹,阻止再次調(diào)用籍嘹。此時dispatch source的擁有者需負(fù)責(zé)釋放dispatch source闪盔。

dispatch_source_t WriteDataToFile(const char* filename)

{

int fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC,

(S_IRUSR | S_IWUSR | S_ISUID | S_ISGID));

if (fd == -1)

return NULL;

fcntl(fd, F_SETFL); // Block during the write.

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_source_t writeSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE,

fd, 0, queue);

if (!writeSource)

{

close(fd);

return NULL;

}

dispatch_source_set_event_handler(writeSource, ^{

size_t bufferSize = MyGetDataSize();

void* buffer = malloc(bufferSize);

size_t actual = MyGetData(buffer, bufferSize);

write(fd, buffer, actual);

free(buffer);

// Cancel and release the dispatch source when done.

dispatch_source_cancel(writeSource);

});

dispatch_source_set_cancel_handler(writeSource, ^{close(fd);});

dispatch_resume(writeSource);

return (writeSource);

}

監(jiān)控文件系統(tǒng)對象

如果需要監(jiān)控文件系統(tǒng)對象的變化,可以設(shè)置一個 DISPATCH_SOURCE_TYPE_VNODE 類型的dispatch source辱士,你可以從這個dispatch source中接收文件刪除泪掀、寫入、重命名等通知颂碘。你還可以得到文件的特定元數(shù)據(jù)信息變化通知异赫。

在dispatch source正在處理事件時,dispatch source中指定的文件描述符必須保持打開狀態(tài)头岔。

下面例子監(jiān)控一個文件的文件名變化祝辣,并在文件名變化時執(zhí)行一些操作(自定義的 MyUpdateFileName 函數(shù))。由于文件描述符專門為dispatch source打開切油,dispatch source安裝了取消處理器來關(guān)閉文件描述符蝙斜。這個例子中的文件描述符關(guān)聯(lián)到底層的文件系統(tǒng)對象,因此同一個dispatch source可以用來檢測多次文件名變化澎胡。

dispatch_source_t MonitorNameChangesToFile(const char* filename)

{

int fd = open(filename, O_EVTONLY);

if (fd == -1)

return NULL;

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,

fd, DISPATCH_VNODE_RENAME, queue);

if (source)

{

// Copy the filename for later use.

int length = strlen(filename);

char* newString = (char*)malloc(length + 1);

newString = strcpy(newString, filename);

dispatch_set_context(source, newString);

// Install the event handler to process the name change

dispatch_source_set_event_handler(source, ^{

const char*? oldFilename = (char*)dispatch_get_context(source);

MyUpdateFileName(oldFilename, fd);

});

// Install a cancellation handler to free the descriptor

// and the stored string.

dispatch_source_set_cancel_handler(source, ^{

char* fileStr = (char*)dispatch_get_context(source);

free(fileStr);

close(fd);

});

// Start processing events.

dispatch_resume(source);

}

else

close(fd);

return source;

}

監(jiān)測信號

應(yīng)用可以接收許多不同類型的信號孕荠,如不可恢復(fù)的錯誤(非法指令)、或重要信息的通知(如子進(jìn)程退出)攻谁。傳統(tǒng)編程中稚伍,應(yīng)用使用 sigaction 函數(shù)安裝信號處理器函數(shù),信號到達(dá)時同步處理信號戚宦。如果你只是想信號到達(dá)時得到通知个曙,并不想實際地處理該信號,可以使用信號dispatch source來異步處理信號受楼。

信號dispatch source不能替代 sigaction 函數(shù)提供的同步信號處理機(jī)制垦搬。同步信號處理器可以捕獲一個信號,并阻止它中止應(yīng)用艳汽。而信號dispatch source只允許你監(jiān)測信號的到達(dá)猴贰。此外,你不能使用信號dispatch source獲取所有類型的信號河狐,如SIGILL, SIGBUS, SIGSEGV信號米绕。

由于信號dispatch source在dispatch queue中異步執(zhí)行,它沒有同步信號處理器的一些限制馋艺。例如信號dispatch source的事件處理器可以調(diào)用任何函數(shù)栅干。靈活性增大的代價是,信號到達(dá)和dispatch source事件處理器被調(diào)用的延遲可能會增大捐祠。

下面例子配置信號dispatch source來處理SIGHUP信號碱鳞,事件處理器調(diào)用 MyProcessSIGHUP 函數(shù),用來處理信號雏赦。

void InstallSignalHandler()

{

// Make sure the signal does not terminate the application.

signal(SIGHUP, SIG_IGN);

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGHUP, 0, queue);

if (source)

{

dispatch_source_set_event_handler(source, ^{

MyProcessSIGHUP();

});

// Start processing signals

dispatch_resume(source);

}

}

監(jiān)控進(jìn)程

進(jìn)程dispatch source可以監(jiān)控特定進(jìn)程的行為劫笙,并適當(dāng)?shù)仨憫?yīng)芙扎。父進(jìn)程可以使用dispatch source來監(jiān)控自己創(chuàng)建的所有子進(jìn)程,例如監(jiān)控子進(jìn)程的死亡;類似地填大,子進(jìn)程也可以使用dispatch source來監(jiān)控父進(jìn)程戒洼,例如在父進(jìn)程退出時自己也退出。

下面例子安裝了一個進(jìn)程dispatch source允华,監(jiān)控父進(jìn)程的終止圈浇。當(dāng)父進(jìn)程退出時,dispatch source設(shè)置一些內(nèi)部狀態(tài)信息靴寂,告知子進(jìn)程自己應(yīng)該退出磷蜀。MySetAppExitFlag 函數(shù)應(yīng)該設(shè)置一個適當(dāng)?shù)臉?biāo)志,允許子進(jìn)程終止百炬。由于dispatch source自主運(yùn)行褐隆,因此自己擁有自己,在程序關(guān)閉時會取消并釋放自己剖踊。

void MonitorParentProcess()

{

pid_t parentPID = getppid();

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC,

parentPID, DISPATCH_PROC_EXIT, queue);

if (source)

{

dispatch_source_set_event_handler(source, ^{

MySetAppExitFlag();

dispatch_source_cancel(source);

dispatch_release(source);

});

dispatch_resume(source);

}

}

取消一個Dispatch Source

除非你顯式地調(diào)用 dispatch_source_cancel 函數(shù)庶弃,dispatch source將一直保持活動,取消一個dispatch source會停止遞送新事件德澈,并且不能撤銷歇攻。因此你通常在取消dispatch source后立即釋放它:

void RemoveDispatchSource(dispatch_source_t mySource)

{

dispatch_source_cancel(mySource);

dispatch_release(mySource);

}

取消一個dispatch source是異步操作,調(diào)用 dispatch_source_cancel 之后梆造,不會再有新的事件被處理缴守,但是正在被dispatch source處理的事件會繼續(xù)被處理完成。在處理完最后的事件之后镇辉,dispatch source會執(zhí)行自己的取消處理器屡穗。

取消處理器是你最后的執(zhí)行機(jī)會,在那里執(zhí)行內(nèi)存或資源的釋放工作摊聋。例如描述符或mach port類型的dispatch source鸡捐,必須提供取消處理器栈暇,用來關(guān)閉描述符或mach port

掛起和繼續(xù)Dispatch Source

你可以使用 dispatch_suspend 和 dispatch_resume 臨時地掛起和繼續(xù)dispatch source的事件遞送麻裁。這兩個函數(shù)分別增加和減少dispatch 對象的掛起計數(shù)。因此源祈,你必須每次 dispatch_suspend 調(diào)用之后煎源,都需要相應(yīng)的 dispatch_resume 才能繼續(xù)事件遞送。

掛起一個dispatch source期間香缺,發(fā)生的任何事件都會被累積手销,直到dispatch source繼續(xù)。但是不會遞送所有事件图张,而是先合并到單一事件呜袁,然后再一次遞送佛嬉。例如你監(jiān)控一個文件的文件名變化扮授,就只會遞送最后一次的變化事件。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末侥钳,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子柄错,更是在濱河造成了極大的恐慌舷夺,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件售貌,死亡現(xiàn)場離奇詭異给猾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)颂跨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門敢伸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人恒削,你說我怎么就攤上這事详拙。” “怎么了蔓同?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵饶辙,是天一觀的道長。 經(jīng)常有香客問我斑粱,道長弃揽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任则北,我火速辦了婚禮矿微,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘尚揣。我一直安慰自己涌矢,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布快骗。 她就那樣靜靜地躺著娜庇,像睡著了一般。 火紅的嫁衣襯著肌膚如雪方篮。 梳的紋絲不亂的頭發(fā)上名秀,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機(jī)與錄音藕溅,去河邊找鬼匕得。 笑死,一個胖子當(dāng)著我的面吹牛巾表,可吹牛的內(nèi)容都是我干的汁掠。 我是一名探鬼主播略吨,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼考阱!你這毒婦竟也來了晋南?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤羔砾,失蹤者是張志新(化名)和其女友劉穎负间,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姜凄,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡政溃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了态秧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片董虱。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖申鱼,靈堂內(nèi)的尸體忽然破棺而出愤诱,到底是詐尸還是另有隱情,我是刑警寧澤捐友,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布淫半,位于F島的核電站,受9級特大地震影響匣砖,放射性物質(zhì)發(fā)生泄漏科吭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一猴鲫、第九天 我趴在偏房一處隱蔽的房頂上張望对人。 院中可真熱鬧,春花似錦拂共、人聲如沸牺弄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽势告。三九已至,卻和暖如春肌厨,著一層夾襖步出監(jiān)牢的瞬間培慌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工柑爸, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盒音。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓表鳍,卻偏偏與公主長得像馅而,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子譬圣,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

推薦閱讀更多精彩內(nèi)容