陳偉 卜慶凱
摘要:軟件在設(shè)計(jì)開發(fā)過程中,都會(huì)考慮同時(shí)執(zhí)行多個(gè)任務(wù)的能力,需要通過多線程的技術(shù)來實(shí)現(xiàn)。同樣在lOS系統(tǒng)中,應(yīng)用軟件也需要具有并發(fā)執(zhí)行多個(gè)任務(wù)的能力。文章內(nèi)容從多線程的概率出發(fā),主要介紹了iOS系統(tǒng)中使用最廣泛的三種多線程技術(shù),即NSThread、GCD、NSOperation。并分別對(duì)三種技術(shù)的使用做了進(jìn)一步的研究與說明,介紹了各個(gè)多線程技術(shù)在使用過程中的一些優(yōu)缺點(diǎn),以及開發(fā)人員在實(shí)際應(yīng)用過程中需要注意的地方。對(duì)從事iOS軟件編程方面對(duì)多線程技術(shù)存在困惑的人具有一定的幫助和指導(dǎo)作用。
關(guān)鍵詞:iOS系統(tǒng);多線程;NSThread;GCD;NSOperation
中圖分類號(hào):TP311.1 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1009-3044(2017)08-0078-03
1背景
目前,阻礙計(jì)算機(jī)性能充分發(fā)揮的主要因素是中心處理器的運(yùn)行速度。在計(jì)算機(jī)技術(shù)快速發(fā)展的前提下,單核處理器已經(jīng)完全滿足不了我們?nèi)粘5氖褂眯枨?。在這種背景下,一些芯片制造商紛紛開始轉(zhuǎn)向多核處理器的設(shè)計(jì),使計(jì)算機(jī)擁有同時(shí)執(zhí)行多個(gè)任務(wù)的能力。美國蘋果公司的iPhone手機(jī)近幾年如此之火爆,不僅是因?yàn)樗恋耐獗砗拓S富的軟件界面,更是因?yàn)樗浞掷昧硕嗪颂幚砥鞯暮诵膬?yōu)勢(shì)。在任何時(shí)候都可以執(zhí)行系統(tǒng)相關(guān)的任務(wù),而且應(yīng)用程序也可以通過使用多線程技術(shù)流暢的執(zhí)行多個(gè)任務(wù),使得iPhone具有卓越的性能和優(yōu)秀的用戶體驗(yàn)。通過使用多線程技術(shù),可以讓比較耗時(shí)的操作放在子線程中執(zhí)行,保證主線程只需要執(zhí)行和用戶界面相關(guān)的操作,不會(huì)有一種界面很“卡”的感覺,確保軟件在使用過程中流暢性較好,增強(qiáng)了用戶體驗(yàn)性能。
2多線程的介紹
2.1進(jìn)程和線程
簡單點(diǎn)概況,進(jìn)程就是計(jì)算機(jī)系統(tǒng)中正在運(yùn)行的程序,是CPU分配資源的基本單位。進(jìn)程與線程之間是相互獨(dú)立的概念,每一個(gè)進(jìn)程只在受保護(hù)的專屬內(nèi)存空間內(nèi)運(yùn)行,而且同一個(gè)進(jìn)程內(nèi)的線程共享進(jìn)程中的數(shù)據(jù)資源。比如我們?cè)陔娔X上同時(shí)打開瀏覽器與音樂播放器,系統(tǒng)就會(huì)自動(dòng)開啟兩個(gè)進(jìn)程。
線程是CPU建立在進(jìn)程基礎(chǔ)之上執(zhí)行任務(wù)的最小單位,且每個(gè)進(jìn)程至少有一條主線程,進(jìn)程中的所有任務(wù)都是在線程中執(zhí)行的。比如用瀏覽器瀏覽網(wǎng)頁,播放器播放音樂,都需要在每個(gè)進(jìn)程中的線程去執(zhí)行。每個(gè)線程中執(zhí)行任務(wù)的方式是串行的,即在同一時(shí)間只可能執(zhí)行一個(gè)任務(wù)。執(zhí)行多個(gè)任務(wù)時(shí)也只能按照任務(wù)的先后順序一個(gè)一個(gè)的執(zhí)行。在Mac和iOS系統(tǒng)中運(yùn)行的應(yīng)用程序每多創(chuàng)建一個(gè)新的線程都需要占用512KB(主線程占用1MB)的內(nèi)存和消耗一定的CPU時(shí)間。
2.2多線程
多線程是指讓應(yīng)用程序的內(nèi)部多個(gè)線程并發(fā)執(zhí)行任務(wù)的一種能提高執(zhí)行效率與資源利用率的技術(shù)。同一時(shí)間段內(nèi)CPU只能處理一條線程上的任務(wù),而多線程并發(fā)執(zhí)行的實(shí)質(zhì)是CPU快速地在多條線程之間的相互切換,如果線程調(diào)度的時(shí)間足夠快,就造成一種多線程并發(fā)執(zhí)行的假象用。如果一個(gè)應(yīng)用程序線程開的太多會(huì)消耗大量的CPU資源,降低程序的性能,每條線程的執(zhí)行頻率和效率也會(huì)大大地降低。
每個(gè)lOS應(yīng)用程序運(yùn)行后,默認(rèn)開啟一條主線程(或稱之為UI線程),其作用是顯示和刷新UI(User Interface/用戶界面),以及處理UI事件(比如點(diǎn)擊事件、滾動(dòng)事件、拖拽事件等)。
3iOS系統(tǒng)中三種多線程技術(shù)
iOS系統(tǒng)中多線程技術(shù)的實(shí)現(xiàn)方案總共有四種,分別為Pthread、NSThread、GCD(Grand Central Dispatch)、NSOperation技術(shù)。Pthread技術(shù)由于其使用難度大等一系列的缺陷,目前在實(shí)際的APP開發(fā)過程中基本上不使用這種淘汰的技術(shù)。因此主要以介紹NSThread、GCD、NSOperation技術(shù)的研究為主。
3.1 NSThread多線程技術(shù)
NSThread是OS X和iOS系統(tǒng)中都提供的一個(gè)以輕量級(jí)實(shí)現(xiàn)的線程對(duì)象。NSThread適應(yīng)于一些輕量級(jí)較簡單的任務(wù),不過用戶需要自己管理線程生命周期以及線程之間的同步。NSThread沒有涉及到線程狀態(tài)、依賴性、線程間的同步問題,當(dāng)多個(gè)線程訪問同一份資源時(shí)會(huì)出現(xiàn)數(shù)據(jù)錯(cuò)亂的現(xiàn)象,這時(shí)我們又要使用互斥鎖來解決這個(gè)問題,而這又會(huì)消耗大量的CPU資源。
NSThread的初始化方法
3)動(dòng)態(tài)方法
動(dòng)態(tài)方法在初始化后,需要手動(dòng)的調(diào)用strut方法才能開啟該線程的真正創(chuàng)建。
其中,target:selector消息發(fā)送的目標(biāo)對(duì)象;selector:線程選擇調(diào)用的方法,最多只能接收一個(gè)參數(shù);argument:傳給selector的唯一參數(shù),也可以是nil。
4)靜態(tài)方法
5)隱式創(chuàng)建線程
靜態(tài)方法和隱式方法在代碼執(zhí)行后會(huì)默認(rèn)開啟一條線程執(zhí)行任務(wù)。
3.2GCD多線程技術(shù)
GCD(Grand Center Dispatch)的實(shí)現(xiàn)是基于c語言的API。GCD是一個(gè)大的主題,負(fù)責(zé)自動(dòng)創(chuàng)建管理線程的生命周期和調(diào)度執(zhí)行任務(wù),提高代碼的執(zhí)行效率與多核的利用率。GCD的API很大程度上基于block,當(dāng)配合block使用時(shí),GCD能發(fā)揮其最大能力。通過使用GCD技術(shù)管理線程,工程師可以不需要再編寫線程代碼,只需要專注于在block或方法中編寫執(zhí)行某項(xiàng)功能的代碼。隊(duì)列這個(gè)概念為GCD的實(shí)現(xiàn)提供了方便,其實(shí)質(zhì)是將長期運(yùn)行的任務(wù)拆分成多個(gè)工作單元,并將這些單元添加到dispath queue(調(diào)度隊(duì)列)中,系統(tǒng)會(huì)自動(dòng)管理這些dispathqueue的生命周期以及任務(wù)的執(zhí)行,不需要我們手動(dòng)管理后臺(tái)線程。而dispath queue也嚴(yán)格遵循FIFO(先進(jìn)先出)原則,串行或并發(fā)地執(zhí)行任務(wù)。
1)獲得全局并發(fā)dispatch queue
并發(fā)dispateh queue可以同時(shí)并行的按照queue加入的先后順序執(zhí)行多個(gè)任務(wù)。系統(tǒng)通常會(huì)默認(rèn)的提供三個(gè)全局享用的并發(fā)dispateh queue給每個(gè)應(yīng)用程序,三個(gè)queue的區(qū)別是優(yōu)先級(jí)的不同。這些queue不用顯式地創(chuàng)建,只需要通過dis-patch_get_global_queue函數(shù)就可以獲取到這三個(gè)queue。
圖4中第一個(gè)參數(shù)用于指定線程的優(yōu)先級(jí),分別使用DIS-PATCH_QUEUE_PRIORITY_HIGH和D/SPATCH_QUEUE_PRI-ORITY_LOW兩個(gè)常量來獲取高和低優(yōu)先級(jí)的兩個(gè)queue;第二個(gè)參數(shù)默認(rèn)0即可。
2)創(chuàng)建串行dispatch queue
創(chuàng)建方法:dispateh_queue_create(const char*_Nullable la-bel,dispatch_queue_attr_t_Nullable attr);
函數(shù)的第一個(gè)參數(shù)是一個(gè)標(biāo)簽,蘋果公司建議我們使用倒置域名來命名隊(duì)列,比如“com.apple.www”。第二個(gè)參數(shù)是隊(duì)列的類型,只能傳參數(shù)DISPATCH_QUEUE_SERIAL(串行隊(duì)列)和NULL,不能傳DISPATCH_QUEUE_CONCURRENT(并發(fā)隊(duì)列)。串行隊(duì)列一次只能執(zhí)行一個(gè)任務(wù),只有當(dāng)前任務(wù)已經(jīng)執(zhí)行完成才會(huì)啟動(dòng)下一個(gè)任務(wù)的執(zhí)行;并發(fā)隊(duì)列則會(huì)盡可能多地啟動(dòng)任務(wù)并發(fā)執(zhí)行,并且只能在異步函數(shù)下有效。
3)添加任務(wù)到queue
異步函數(shù):dispatch_async(dispatch_queue_t queue,dis-patch_block_t blockk
同步函數(shù):dispatch_sync(dispatch_queue_t queue,DIS-PA TCH_NOESCAPE dispateh_block_t block);
其中第一個(gè)參數(shù)表示已經(jīng)創(chuàng)建的隊(duì)列名稱,第二個(gè)參數(shù)是需要實(shí)現(xiàn)某些具體功能的代碼段(即block任務(wù))。異步函數(shù)具備開啟新線程執(zhí)行任務(wù)的能力,在調(diào)用時(shí)會(huì)立即返回,同時(shí)隊(duì)列會(huì)在輪到這個(gè)block執(zhí)行時(shí)后臺(tái)異步執(zhí)行這個(gè)任務(wù),這樣就可以調(diào)用線程繼續(xù)去做其它事情。而同步函數(shù)不具備開新線程的能力,在調(diào)用時(shí)會(huì)在當(dāng)前線程等待block中的代碼執(zhí)行完成后返回,容易出現(xiàn)阻塞當(dāng)前調(diào)用線程的現(xiàn)象,而在串行queue中會(huì)導(dǎo)致死鎖。
在傳統(tǒng)的多線程編程中,你可能有一個(gè)對(duì)象要被多個(gè)線程使用,這時(shí)候你需要一個(gè)互斥鎖來保護(hù)這個(gè)對(duì)象。然而在GCD中,用戶隊(duì)列完全可以替代互斥鎖來完成同步機(jī)制,就不可能意外寫出具有不成對(duì)Lock(鎖)的代碼。
3.3 NSOperation和NSOperationQueue
配合使用NSOperation和NSOperationQueue也能實(shí)現(xiàn)多線程的功能。NSOperation實(shí)例中已經(jīng)封裝好了開發(fā)過程中需要執(zhí)行的操作以及所需的數(shù)據(jù),能夠以并發(fā)或非并發(fā)的方式執(zhí)行這個(gè)操作。NSOperation是一個(gè)抽象的基類,本身不具備把操作進(jìn)行封裝的能力,必須使用它的子類才能實(shí)現(xiàn)。目前有三種可以使用NSOperation子類的方法,F(xiàn)oundation框架為我們提供了兩個(gè)具體子類直接供我們使用,即NShwocationOperation和NSBlockOperation方法;另外一個(gè)是自定義子類繼承自NSOper-ation,實(shí)現(xiàn)內(nèi)部相應(yīng)的方法實(shí)現(xiàn)。
1)創(chuàng)建NSInvocationOperation對(duì)象
創(chuàng)建方法:-(nullable instancetype)init WithTarget:(id)targetselector:(SEL)sel object:(nullable id)arg;
其中,三個(gè)參數(shù)分別表示目標(biāo)對(duì)象、調(diào)用方法以及方法需要傳遞的參數(shù)。在使用時(shí)需要調(diào)用start方法開始執(zhí)行操作。默認(rèn)情況下,調(diào)用start方法后并不會(huì)開一條新線程去執(zhí)行操作,而是在當(dāng)前線程同步執(zhí)行操作,直到任務(wù)完成。如果想要中途取消操作,可以調(diào)用cancel方法。
上面是調(diào)用self均test方法,通過調(diào)用start方法開始執(zhí)行任務(wù)。
2)創(chuàng)建NSBlockOperation對(duì)象
創(chuàng)建方法:+(instancetype)blockOperationWithBlock:(void (^)(void))block;
這種方法能夠并發(fā)地執(zhí)行一個(gè)或多個(gè)block對(duì)象,所有相關(guān)的block都執(zhí)行完之后,操作才算完成。其次,通過調(diào)用ad-dExecutionBlock:方法可以添加更多的block操作。
圖7中首先創(chuàng)建了一個(gè)名稱為operation的NSBlockOpera-tion對(duì)象,通過addExecationBlock方法添加三個(gè)新的block操作,即NSBlockOperation封裝的操作數(shù)大于1,這樣可以使4個(gè)block操作并發(fā)的在不同線程中執(zhí)行。
NSOperation雖然可以調(diào)用start方法來執(zhí)行任務(wù),但默認(rèn)是同步執(zhí)行的,如果將NSOperation添加到NSOperationQueue(操作隊(duì)列)中,系統(tǒng)會(huì)自動(dòng)異步執(zhí)行NSOperation中的操作。
對(duì)于上述NSlnvocationOperation和NSBlockOperation方法,我們需要?jiǎng)?chuàng)建一個(gè)NSOperationQueue對(duì)象,將封裝好操作代碼的NSOperation對(duì)象添加到NSOperationQueue中,系統(tǒng)會(huì)自動(dòng)將NSOperationQueue中的NSOperation對(duì)象提取出來,然后將NSOperation封裝的操作放到一條新線程中執(zhí)行。
3)自定義NSOperation對(duì)象
如果NShwocationOperation和NSBlockOperation對(duì)象不能滿足我們的任務(wù)需求,可以自定義一個(gè)直接繼承自NSOperation的對(duì)象,并在其中添加任何想要的方法。
圖8中的CHWOperation是我們自定義的繼承自NSOpera-tion的對(duì)象,在定義非并發(fā)的NSOperation對(duì)象只需要重載-(Void)main方法,在這個(gè)方法里面添加執(zhí)行主任務(wù)的代碼,并正確地響應(yīng)任務(wù)取消事件;對(duì)于并發(fā)NSOperation,需要重寫NSOperation的多個(gè)基本方法進(jìn)行實(shí)現(xiàn)。
4)其他相關(guān)用法
有時(shí),僅僅依靠這些方法并不能滿足我們的需求,所以我們需要通過其他方法使任務(wù)更加合理有序的執(zhí)行。通過-(NSInte-ger)maxConcurrentOperationCount方法,可以設(shè)置同時(shí)執(zhí)行任務(wù)的最大并發(fā)數(shù)。當(dāng)隊(duì)列有取消、暫停、恢復(fù)需求時(shí),調(diào)用-(void)cancelAllOperatious方法取消所有隊(duì)列正在執(zhí)行的任務(wù),-(void)cancel方法取消單個(gè)隊(duì)列操作,而NSOperation對(duì)象會(huì)定期地調(diào)用isCancelled方法檢測(cè)操作是否已經(jīng)被取消,這種方法本身非常輕量即使是頻繁地調(diào)用也不會(huì)產(chǎn)生比較大的性能損失;設(shè)置Suspended方法的BOOL值,YES表示暫停,NO表示取消。
NSOperation之間還可以設(shè)置依賴來保證任務(wù)的執(zhí)行順序,比如要讓操作B在操作A完成之后再執(zhí)行,可以設(shè)置[opera-tionB addDependency:operationA],即操作B依賴于操作A。同樣在不同queue之間的NSOperation也可以創(chuàng)建依賴關(guān)系。不過值得我們注意的是任何兩個(gè)操作之間不能相互依賴。
4結(jié)束語
根據(jù)在iOS開發(fā)學(xué)習(xí)過程中出現(xiàn)的問難,本文詳細(xì)討論了iOS系統(tǒng)中三種多線程技術(shù)的使用,對(duì)于在iOS編程中對(duì)多線程有疑問的開發(fā)者具有一定的幫助作用。而通過對(duì)三種多線程技術(shù)的研究比較,在實(shí)際的開發(fā)過程中,開發(fā)者應(yīng)該盡可能的少用需要人工管理生命周期的NSThread技術(shù),轉(zhuǎn)而使用具有自動(dòng)管理生命周期功能的GCD和NSOperation多線程技術(shù)。此外,iOS系統(tǒng)目前已經(jīng)更新到了iOS10,以后技術(shù)還會(huì)不斷提高,目前的多線程技術(shù)也肯定滿足不了以后的軟件需求。所以,開發(fā)人員需要不斷提高自身的技術(shù)能力,結(jié)合Apple公司提供的方法,研究出更加完善的多線程技術(shù),以此來推動(dòng)lOS系統(tǒng)的不斷發(fā)展。