李嵐
摘要:如今移動(dòng)設(shè)備已經(jīng)進(jìn)入了多核心CPU 時(shí)代,并且隨著時(shí)間的推移,CPU 的核心數(shù)只會(huì)增加不會(huì)減少。作為軟件開發(fā)者需要盡可能地提高應(yīng)用的并發(fā)性,充分利用多核心CPU 的性能。iOS是由蘋果公司開發(fā)的移動(dòng)操作系統(tǒng),在 iOS 開發(fā)中,主要通過 Dispatch Queues 、Dispatch Sources和Operation Queues 來提高應(yīng)用的并發(fā)性。該文對(duì)針對(duì)這三種工具的使用方法及技巧進(jìn)行了研究,總結(jié)出提高多線程編程的效率的方法。
關(guān)鍵詞:iOS ;并發(fā)程序;多線程
中圖分類號(hào):TP311 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1009-3044(2016)09-0083-02
iOS是蘋果公司開發(fā)的移動(dòng)操作系統(tǒng),隨著蘋果電子產(chǎn)品的風(fēng)靡世界,iOS版本的不斷更新,“果粉”們對(duì)新應(yīng)用的需求量日益增加,越來越多的程序員選擇加入到iOS應(yīng)用開發(fā)的行列中。 在如今多核心CPU時(shí)代,如何提高所開發(fā)應(yīng)用的并發(fā)性,充分利用多核心CPU,是軟件開發(fā)者需要思考的問題。
Grand Central Dispatch (GCD)是蘋果公司開發(fā)的一個(gè)多核編程的解決方法。該方法的需要在iOS4.0以上的版本中使用。
Dispatch Queues、Dispatch Source 和Operation Queue是iOS中的多線程編程中通常使用的三種方法,開發(fā)過程通過使用它們可以提高應(yīng)用的并發(fā)性。因?yàn)樗鼈兌寄堋白詣?dòng)地”解決在什么時(shí)間、何種系統(tǒng)可用資源條件下建立及維護(hù)線程的支持工作。同時(shí),向程序員隱藏了線程這個(gè)名稱,并以工作隊(duì)列這樣抽象的對(duì)象,使得并發(fā)程序更加方便實(shí)現(xiàn)。其中Dispatch Queues和Dispatch Source兩種方法是基于GCD的。下面依次介紹三種方法的使用特點(diǎn)及技巧。
1 Dispatch Queue的使用
Dispatch Queue作為對(duì)象,接受任務(wù),將任務(wù)以先進(jìn)先出的順序執(zhí)行。并且它還有著編程簡單、能夠提供線程池的管理、節(jié)約內(nèi)存空間的使用、不trap內(nèi)核、無死鎖、在順序的隊(duì)列中比直接加鎖同步的執(zhí)行速度快等一系列的優(yōu)點(diǎn)。
Dispatch Queue可以并發(fā)執(zhí)行任務(wù),也能夠串行執(zhí)行任務(wù)。并發(fā)任務(wù)只要是在隊(duì)列中的工作,都會(huì)被陸續(xù)地扔在各個(gè)Thread中去執(zhí)行,它們之間相互獨(dú)立,不需要等待其中一個(gè)結(jié)束了,再由隊(duì)列扔出一個(gè)新的任務(wù)到Thread上去執(zhí)行,而且是同時(shí)在各個(gè)Thread中各自執(zhí)行。串行任務(wù)則意味著在這個(gè)隊(duì)列中的各種操作,需要“一個(gè)一個(gè)”地按順序進(jìn)行執(zhí)行,下一個(gè)任務(wù)需要等待上一個(gè)任務(wù)執(zhí)行結(jié)束后,才會(huì)進(jìn)入執(zhí)行。
1.1 Dispatch Queue中隊(duì)列的三種分類
Dispatch Queue中隊(duì)列的隊(duì)列分為順序隊(duì)列、并發(fā)隊(duì)列和Main dispatch queue三種。
所謂順序隊(duì)列即是在一個(gè)queue中,每次僅1個(gè)任務(wù)在執(zhí)行。
并發(fā)隊(duì)列即是在一個(gè)queue中,每次多于1個(gè)任務(wù)在執(zhí)行,而這個(gè)多的數(shù)量由queue自己決定,它是根據(jù)相應(yīng)的系統(tǒng)資源來決定的,資源多則數(shù)量多,資源少則數(shù)量少。
main dispatch queue,它是一個(gè)順序的隊(duì)列,它在main thread上執(zhí)行,程序員也可以稱main thread為”程序同步關(guān)鍵點(diǎn)“。
1.2 某些關(guān)鍵的“優(yōu)點(diǎn)”
1)如果通過線程編程,其中2個(gè)線程上的task,都去訪問共享資源。通過使用lock來進(jìn)行線程的同步,額外的會(huì)造成相應(yīng)的內(nèi)核中斷。而相應(yīng)的,對(duì)于Dispatch Queue而言,可以使用順序的queue來進(jìn)行處理,從而提高效率。
2)多個(gè)Dispatch Queue的之間是并發(fā)的執(zhí)行的,若是順序的話,單個(gè)Dispatch Queue之內(nèi)是順序的。
3)Dispatch Queue會(huì)根據(jù)系統(tǒng)狀況來選擇create thread的數(shù)目,不會(huì)無限制擴(kuò)張。
1.3 block的問題
1)block看上去像函數(shù)指針,其實(shí)它是一個(gè)基本的數(shù)據(jù)結(jié)構(gòu)對(duì)象,由編譯器建立和管理。
2)block可以訪問其父作用域上的變量,如果不加__block,則是把這個(gè)變量拷貝到block自身的堆內(nèi)存空間上。
3)block不要去使用父作用域上的指針變量,這些指針指向的對(duì)象的生命周期是由caller來決定的。也就是說,block不要引用一個(gè)野指針。
4)queue會(huì)copy一個(gè)block到其中,當(dāng)block執(zhí)行完畢,它會(huì)做release的處理。
1.4 queue 需要release的問題
全局的queue,不需要retain和release。自己create的queue,需要release。
1.5 queue的自身的context的設(shè)定
每一個(gè)queue都可以設(shè)定用戶自定義的context數(shù)據(jù),可以通過調(diào)用dispatch_set_context(queue,contextdata)設(shè)置context數(shù)據(jù),通過dispatch_set_finalizer_f來設(shè)定怎樣釋放其對(duì)應(yīng)queue的contextdata的函數(shù)指針。
1.6 dispatch group
dispatch group是一個(gè)lock的方法,可以lock一個(gè)或一系列task完成的條件,使得后續(xù)代碼要等待某task執(zhí)行完畢后,再繼續(xù)執(zhí)行。
1.7 線程安全
1)對(duì)于Dispatch Queue自身來說,線程是安全的。
2)不要在同一個(gè)queue中,調(diào)用dispatch_sync函數(shù),如果這樣調(diào)用,將會(huì)出現(xiàn)死鎖的現(xiàn)象。例如:在a中同步調(diào)用b,結(jié)果b的block一直等待a的資源,而b不執(zhí)行完畢,a是沒辦法繼續(xù)向下執(zhí)行
2 Dispatch Source的使用
Dispatch Source是GCD的一部分,是用于事件響應(yīng)的隊(duì)列。用于監(jiān)聽系統(tǒng)push上來的timer、 signal以及各種event,一旦發(fā)生,立即把關(guān)聯(lián)的block對(duì)象扔到Dispatch Queue上執(zhí)行。
2.1 創(chuàng)建Dispatch Source
需要?jiǎng)?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è)置一個(gè)事件處理器
對(duì)于定時(shí)器源,使用 dispatch_source_set_timer 函數(shù)設(shè)置定時(shí)器信息
為Dispatch Source賦予一個(gè)取消處理器(可選)調(diào)用 dispatch_resume 函數(shù)開始處理事件由于Dispatch Source必須進(jìn)行額外的配置才能被使用,dispatch_source_create 函數(shù)返回的Dispatch Source將處于掛起狀態(tài)。此時(shí)Dispatch Source會(huì)接收事件,但是不會(huì)進(jìn)行處理。這時(shí)候你可以安裝事件處理器,并執(zhí)行額外的配置。
2.2 編寫和安裝一個(gè)事件處理器
需要定義一個(gè)事件處理器來處理事件,可以是函數(shù)或block對(duì)象,并使用 dispatch_source_set_event_handler 或 dispatch_source_set_event_handler_f 安裝事件處理器。事件到達(dá)時(shí),Dispatch Source會(huì)提交你的事件處理器到指定的Dispatch Queue,由queue執(zhí)行事件處理器。
事件處理器的代碼負(fù)責(zé)處理所有到達(dá)的事件。如果事件處理器已經(jīng)在queue中并等待處理已經(jīng)到達(dá)的事件,如果此時(shí)又來了一個(gè)新事件,Dispatch Source會(huì)合并這兩個(gè)事件。事件處理器通常只能看到最新事件的信息,不過某些類型的Dispatch Source也能獲得已經(jīng)發(fā)生以及合并的事件信息。
如果事件處理器已經(jīng)開始執(zhí)行,一個(gè)或多個(gè)新事件到達(dá),Dispatch Source會(huì)保留這些事件,直到前面的事件處理器完成執(zhí)行。然后以新事件再次提交處理器到queue。
函數(shù)事件處理器有一個(gè)context指針指向Dispatch Source對(duì)象,沒有返回值。Block事件處理器沒有參數(shù),也沒有返回值。
3 Operation Queue的使用
Operation Queue的實(shí)現(xiàn)機(jī)制,是使用NSOperationQueue和NSOperation來完成的。
通常,Operation Queue中的執(zhí)行對(duì)象是NSOperation類的子類的對(duì)象(NSOperation類是抽象的)。
相對(duì)于Dispatch Queue的FIFO而言,這里的順序,依賴于NSOperation之間的依賴性來選擇執(zhí)行順序的。因而它是按照一定順序來執(zhí)行的,并且是并發(fā)拋出的。
4 結(jié)論
4.1 使用并發(fā)、多線程幾種情況
1)把任務(wù)進(jìn)行分解,如果子任務(wù)之間的行為是相互獨(dú)立的,那么可以確認(rèn)在子任務(wù)之間可以是多線程執(zhí)行的。
2)如果子任務(wù)之間相互存在依賴關(guān)系。例如:下一個(gè)子任務(wù)的輸出必須通過上一個(gè)子任務(wù)的輸入來獲取,則最好不用并發(fā)異步。
3)獨(dú)立的子任務(wù)確認(rèn)后,就根據(jù)順序還是并發(fā)的需求,塞入相應(yīng)的group就好。
4)對(duì)于operation object而言,如果要設(shè)計(jì)成順序執(zhí)行的情況,就必須要設(shè)置任務(wù)之間的依賴,如果用block,就在Dispatch Queue中設(shè)置順序即可,其他默認(rèn)并發(fā)。
4.2 提高多線程編程效率的技巧:
1)盡量不要讓共享資源lock住線程的并發(fā)。盡量使用線程的局部value來代替共享資源。變成可重入。
2)禁止使用lock。如果并發(fā)需要lock來同步,那么盡量使用serial queue來進(jìn)行隊(duì)列的方式來設(shè)計(jì)軟件架構(gòu)。但是注意,如果iOS的一些回調(diào)函數(shù),或者通知函數(shù),打回來時(shí)就是開啟新線程,那么就要考慮對(duì)new Thread進(jìn)行加入lock了。但是這是自己沒法控制的時(shí)候,這樣做的,如果代碼部分是可以自己控制的,則盡量禁止使用lock機(jī)制,或者信號(hào)量。如果同時(shí)建立1k個(gè)operation對(duì)象,然后提交到operation queue中,那么會(huì)引起不斷的內(nèi)存換頁。
參考文獻(xiàn):
[1] Rob Napier,Mugunth Kumar .iOS編程實(shí)戰(zhàn)[M]. 美團(tuán)移動(dòng),譯.北京: 人民郵電出版社,2014.
[2] 關(guān)東升. iOS開發(fā)指南:從零基礎(chǔ)到App Store上架[M].2版. 北京:人民郵電出版社,2014.
[3] Kazuki Sakamoto, Tomohiko Furumoto . Objective-C高級(jí)編程 iOS與OS X多線程和內(nèi)存管理[M].黎華,譯. 北京:人民郵電出版社,2013.
[4] Erica Sadun ,iOS Auto Layout Demystified ,Second Edition[M]. 2nd ed,Addison-Wesley Educational Publishers Inc, 2013.