自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

GCD介紹(三):Dispatch Sources

移動(dòng)開(kāi)發(fā) iOS
本文為大家講述怎樣使用GCD的dispatch source功能來(lái)監(jiān)視文件描述符、計(jì)時(shí)器、聯(lián)結(jié)的用戶(hù)事件以及其他類(lèi)似的行為。由于dispatch source完全與dispatch queue相集成,所以你可以使用任意的dispatch queue。你可以將一個(gè)dispatch source的句柄在主線程中執(zhí)行、在全局隊(duì)列中并發(fā)執(zhí)行、或者在用戶(hù)隊(duì)列中串行執(zhí)行(執(zhí)行時(shí)會(huì)將程序的其他模塊的運(yùn)算考慮在內(nèi))。

[[77411]]

何為Dispatch Sources

簡(jiǎn)單來(lái)說(shuō),dispatch source是一個(gè)監(jiān)視某些類(lèi)型事件的對(duì)象。當(dāng)這些事件發(fā)生時(shí),它自動(dòng)將一個(gè)block放入一個(gè)dispatch queue的執(zhí)行例程中。

說(shuō)的貌似有點(diǎn)不清不楚。我們到底討論哪些事件類(lèi)型?

下面是GCD 10.6.0版本支持的事件:

  1. Mach port send right state changes.
  2. Mach port receive right state changes.
  3. External process state change.
  4. File descriptor ready for read.
  5. File descriptor ready for write.
  6. Filesystem node event.
  7. POSIX signal.
  8. Custom timer.
  9. Custom event.

這是一堆很有用的東西,它支持所有kqueue所支持的事件(kqueue是什么?見(jiàn)http://en.wikipedia.org/wiki/Kqueue)以及mach(mach是什么?見(jiàn)http://en.wikipedia.org/wiki/Mach_(kernel))端口、內(nèi)建計(jì)時(shí)器支持(這樣我們就不用使用超時(shí)參數(shù)來(lái)創(chuàng)建自己的計(jì)時(shí)器)和用戶(hù)事件。

用戶(hù)事件

這些事件里面多數(shù)都可以從名字中看出含義,但是你可能想知道啥叫用戶(hù)事件。簡(jiǎn)單地說(shuō),這種事件是由你調(diào)用dispatch_source_merge_data函數(shù)來(lái)向自己發(fā)出的信號(hào)。

這個(gè)名字對(duì)于一個(gè)發(fā)出事件信號(hào)的函數(shù)來(lái)說(shuō),太怪異了。這個(gè)名字的來(lái)由是GCD會(huì)在事件句柄被執(zhí)行之前自動(dòng)將多個(gè)事件進(jìn)行聯(lián)結(jié)。你可以將數(shù)據(jù)“拼接”至dispatch source中任意次,并且如果dispatch queue在這期間繁忙的話,GCD只會(huì)調(diào)用該句柄一次(不要覺(jué)得這樣會(huì)有問(wèn)題,看完下面的內(nèi)容你就明白了)。

用戶(hù)事件有兩種: DISPATCH_SOURCE_TYPE_DATA_ADD 和 DISPATCH_SOURCE_TYPE_DATA_OR.用戶(hù)事件源有個(gè) unsigned long data屬性,我們將一個(gè) unsigned long傳入 dispatch_source_merge_data。當(dāng)使用 _ADD版本時(shí),事件在聯(lián)結(jié)時(shí)會(huì)把這些數(shù)字相加。當(dāng)使用 _OR版本時(shí),事件在聯(lián)結(jié)時(shí)會(huì)把這些數(shù)字邏輯與運(yùn)算。當(dāng)事件句柄執(zhí)行時(shí),我們可以使用dispatch_source_get_data函數(shù)訪問(wèn)當(dāng)前值,然后這個(gè)值會(huì)被重置為0。

讓我假設(shè)一種情況。假設(shè)一些異步執(zhí)行的代碼會(huì)更新一個(gè)進(jìn)度條。因?yàn)橹骶€程只不過(guò)是GCD的另一個(gè)dispatch queue而已,所以我們可以將GUI更新工作push到主線程中。然而,這些事件可能會(huì)有一大堆,我們不想對(duì)GUI進(jìn)行頻繁而累贅的更新,理想的情況是當(dāng)主線程繁忙時(shí)將所有的改變聯(lián)結(jié)起來(lái)。

用dispatch source就完美了,使用DISPATCH_SOURCE_TYPE_DATA_ADD,我們可以將工作拼接起來(lái),然后主線程可以知道從上一次處理完事件到現(xiàn)在一共發(fā)生了多少改變,然后將這一整段改變一次更新至進(jìn)度條。

啥也不說(shuō)了,上代碼:

  1. dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue()); 
  2. dispatch_source_set_event_handler(source, ^{ 
  3.     [progressIndicator incrementBy:dispatch_source_get_data(source)]; 
  4. }); 
  5. dispatch_resume(source); 
  6.  
  7. dispatch_apply([array count], globalQueue, ^(size_t index) { 
  8.     // do some work on data at index 
  9.     dispatch_source_merge_data(source, 1); 
  10. }); 

(對(duì)于這段代碼,我很想說(shuō)點(diǎn)什么,我第一次用dispatch source時(shí),我糾結(jié)了很久很久,真讓人蛋疼:Dispatch source啟動(dòng)時(shí)默認(rèn)狀態(tài)是掛起的,我們創(chuàng)建完畢之后得主動(dòng)恢復(fù),否則事件不會(huì)被傳遞,也不會(huì)被執(zhí)行

假設(shè)你已經(jīng)將進(jìn)度條的min/max值設(shè)置好了,那么這段代碼就完美了。數(shù)據(jù)會(huì)被并發(fā)處理。當(dāng)每一段數(shù)據(jù)完成后,會(huì)通知dispatch source并將dispatch source data加1,這樣我們就認(rèn)為一個(gè)單元的工作完成了。事件句柄根據(jù)已完成的工作單元來(lái)更新進(jìn)度條。若主線程比較空閑并且這些工作單元進(jìn)行的比較慢,那么事件句柄會(huì)在每個(gè)工作單元完成的時(shí)候被調(diào)用,實(shí)時(shí)更新。如果主線程忙于其他工作,或者工作單元完成速度很快,那么完成事件會(huì)被聯(lián)結(jié)起來(lái),導(dǎo)致進(jìn)度條只在主線程變得可用時(shí)才被更新,并且一次將積累的改變更新至GUI。

現(xiàn)在你可能會(huì)想,聽(tīng)起來(lái)倒是不錯(cuò),但是要是我不想讓事件被聯(lián)結(jié)呢?有時(shí)候你可能想讓每一次信號(hào)都會(huì)引起響應(yīng),什么后臺(tái)的智能玩意兒統(tǒng)統(tǒng)不要。啊。。其實(shí)很簡(jiǎn)單的,別把自己繞進(jìn)去了。如果你想讓每一個(gè)信號(hào)都得到響應(yīng),那使用dispatch_async函數(shù)不就行了。實(shí)際上,使用的dispatch source而不使用dispatch_async的唯一原因就是利用聯(lián)結(jié)的優(yōu)勢(shì)。

內(nèi)建事件

上面就是怎樣使用用戶(hù)事件,那么內(nèi)建事件呢?看看下面這個(gè)例子,用GCD讀取標(biāo)準(zhǔn)輸入:

  1.     dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 
  2.     dispatch_source_t stdinSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, 
  3.                                                            STDIN_FILENO, 
  4.                                                            0, 
  5. globalQueue); 
  6.     dispatch_source_set_event_handler(stdinSource, ^{ 
  7.         char buf[1024]; 
  8.         int len = read(STDIN_FILENO, buf, sizeof(buf)); 
  9.         if(len > 0) 
  10.             NSLog(@"Got data from stdin: %.*s", len, buf); 
  11.     }); 
  12.     dispatch_resume(stdinSource); 

簡(jiǎn)單的要死!因?yàn)槲覀兪褂玫氖侨株?duì)列,句柄自動(dòng)在后臺(tái)執(zhí)行,與程序的其他部分并行,這意味著對(duì)這種情況的提速:事件進(jìn)入程序時(shí),程序正在處理其他事務(wù)。

這是標(biāo)準(zhǔn)的UNIX方式來(lái)處理事務(wù)的好處,不用去寫(xiě)loop。如果使用經(jīng)典的 read調(diào)用,我們還得萬(wàn)分留神,因?yàn)榉祷氐臄?shù)據(jù)可能比請(qǐng)求的少,還得忍受無(wú)厘頭的“errors”,比如 EINTR (系統(tǒng)調(diào)用中斷)。使用GCD,我們啥都不用管,就從這些蛋疼的情況里解脫了。如果我們?cè)谖募枋龇辛粝铝宋醋x取的數(shù)據(jù),GCD會(huì)再次調(diào)用我們的句柄。

對(duì)于標(biāo)準(zhǔn)輸入,這沒(méi)什么問(wèn)題,但是對(duì)于其他文件描述符,我們必須考慮在完成讀寫(xiě)之后怎樣清除描述符。對(duì)于dispatch source還處于活躍狀態(tài)時(shí),我們決不能關(guān)閉描述符。如果另一個(gè)文件描述符被創(chuàng)建了(可能是另一個(gè)線程創(chuàng)建的)并且新的描述符剛好被分配了相同的數(shù)字,那么你的dispatch source可能會(huì)在不應(yīng)該的時(shí)候突然進(jìn)入讀寫(xiě)狀態(tài)。de這個(gè)bug可不是什么好玩的事兒。

適當(dāng)?shù)那宄绞绞鞘褂?nbsp;dispatch_source_set_cancel_handler,并傳入一個(gè)block來(lái)關(guān)閉文件描述符。然后我們使用 dispatch_source_cancel來(lái)取消dispatch source,使得句柄被調(diào)用,然后文件描述符被關(guān)閉。

使用其他dispatch source類(lèi)型也差不多??偟膩?lái)說(shuō),你提供一個(gè)source(mach port、文件描述符、進(jìn)程ID等等)的區(qū)分符來(lái)作為diapatch source的句柄。mask參數(shù)通常不會(huì)被使用,但是對(duì)于 DISPATCH_SOURCE_TYPE_PROC 來(lái)說(shuō)mask指的是我們想要接受哪一種進(jìn)程事件。然后我們提供一個(gè)句柄,然后恢復(fù)這個(gè)source(前面我加粗字體所說(shuō)的,得先恢復(fù)),搞定。dispatch source也提供一個(gè)特定于source的data,我們使用 dispatch_source_get_data函數(shù)來(lái)訪問(wèn)它。例如,文件描述符會(huì)給出大致可用的字節(jié)數(shù)。進(jìn)程source會(huì)給出上次調(diào)用之后發(fā)生的事件的mask。具體每種source給出的data的含義,看man page吧。

計(jì)時(shí)器

計(jì)時(shí)器事件稍有不同。它們不使用handle/mask參數(shù),計(jì)時(shí)器事件使用另外一個(gè)函數(shù) dispatch_source_set_timer 來(lái)配置計(jì)時(shí)器。這個(gè)函數(shù)使用三個(gè)參數(shù)來(lái)控制計(jì)時(shí)器觸發(fā):

start參數(shù)控制計(jì)時(shí)器第一次觸發(fā)的時(shí)刻。參數(shù)類(lèi)型是 dispatch_time_t,這是一個(gè)opaque類(lèi)型,我們不能直接操作它。我們得需要 dispatch_time 和  dispatch_walltime 函數(shù)來(lái)創(chuàng)建它們。另外,常量  DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER 通常很有用。

interval參數(shù)沒(méi)什么好解釋的。

leeway參數(shù)比較有意思。這個(gè)參數(shù)告訴系統(tǒng)我們需要計(jì)時(shí)器觸發(fā)的精準(zhǔn)程度。所有的計(jì)時(shí)器都不會(huì)保證100%精準(zhǔn),這個(gè)參數(shù)用來(lái)告訴系統(tǒng)你希望系統(tǒng)保證精準(zhǔn)的努力程度。如果你希望一個(gè)計(jì)時(shí)器沒(méi)五秒觸發(fā)一次,并且越準(zhǔn)越好,那么你傳遞0為參數(shù)。另外,如果是一個(gè)周期性任務(wù),比如檢查email,那么你會(huì)希望每十分鐘檢查一次,但是不用那么精準(zhǔn)。所以你可以傳入60,告訴系統(tǒng)60秒的誤差是可接受的。

這樣有什么意義呢?簡(jiǎn)單來(lái)說(shuō),就是降低資源消耗。如果系統(tǒng)可以讓cpu休息足夠長(zhǎng)的時(shí)間,并在每次醒來(lái)的時(shí)候執(zhí)行一個(gè)任務(wù)集合,而不是不斷的醒來(lái)睡去以執(zhí)行任務(wù),那么系統(tǒng)會(huì)更高效。如果傳入一個(gè)比較大的leeway給你的計(jì)時(shí)器,意味著你允許系統(tǒng)拖延你的計(jì)時(shí)器來(lái)將計(jì)時(shí)器任務(wù)與其他任務(wù)聯(lián)合起來(lái)一起執(zhí)行。

總結(jié)

現(xiàn)在你知道怎樣使用GCD的dispatch source功能來(lái)監(jiān)視文件描述符、計(jì)時(shí)器、聯(lián)結(jié)的用戶(hù)事件以及其他類(lèi)似的行為。由于dispatch source完全與dispatch queue相集成,所以你可以使用任意的dispatch queue。你可以將一個(gè)dispatch source的句柄在主線程中執(zhí)行、在全局隊(duì)列中并發(fā)執(zhí)行、或者在用戶(hù)隊(duì)列中串行執(zhí)行(執(zhí)行時(shí)會(huì)將程序的其他模塊的運(yùn)算考慮在內(nèi))。

下一篇我會(huì)討論如何對(duì)dispatch queue進(jìn)行掛起、恢復(fù)、重定目標(biāo)操作;如何使用dispatch semaphore;如何使用GCD的一次性初始化功能。

責(zé)任編輯:閆佳明 來(lái)源: dreamingwish
相關(guān)推薦

2013-07-15 15:51:32

iOS多線程GCD基本概念Dispatch Qu

2013-07-15 16:28:15

iOS多線程GCD介紹Dispatch Qu

2013-07-15 16:00:59

2015-06-26 09:29:12

Grand Centr

2013-07-15 15:23:03

iOS多線程GCD

2013-07-15 16:55:54

iOS多線程GCD實(shí)戰(zhàn)資源競(jìng)爭(zhēng)

2023-02-03 14:41:03

地址翻譯模式loongarch

2012-09-20 10:50:34

IBMdw

2011-08-15 11:13:06

IOS開(kāi)發(fā)并發(fā)Dispatch Qu

2010-03-17 17:16:46

Fedora 常用軟件

2011-04-26 11:03:36

三星打印機(jī)

2010-04-26 17:15:13

Oracle優(yōu)化器

2011-07-19 10:26:49

Active Dire回收站

2010-04-20 12:00:01

負(fù)載均衡技術(shù)

2010-04-19 09:31:44

KDE

2020-12-18 10:04:52

API漏洞應(yīng)用程序編程接口

2010-03-05 11:18:52

Linux shell

2012-01-06 11:13:02

SinatraJava框架

2013-07-19 14:00:13

iOS中BlockiOS開(kāi)發(fā)學(xué)習(xí)

2010-03-01 15:21:57

WCF死鎖
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)