摘 要:iOS平臺為適應(yīng)多核心處理器的發(fā)展,對多線程技術(shù)提供了很好的支持。在iOS應(yīng)用開發(fā)中靈活運用多線程技術(shù)可以提高iPhone處理器的利用率,最終提高手機運行效率,給用戶更好的體驗。通過介紹多線程技術(shù)的相關(guān)知識,闡述了多線程開發(fā)的優(yōu)缺點,列舉了iOS支持的多種多線程技術(shù)。最后,結(jié)合實例重點闡述了GCD多線程技術(shù)在iOS開發(fā)中的應(yīng)用,對理解和掌握iOS多線程開發(fā)有一定的幫助。
關(guān)鍵詞:多線程;iOS;開發(fā);GCD
中圖分類號:TP311.1 文獻標識碼:A
文章編號:2096-1472(2018)-11-38-04
1 引言(Introduction)
隨著用戶對計算機計算能力需求的不斷追求,為了適應(yīng)用戶的更高要求,多核心處理器已被廣泛使用在各類電子設(shè)備上,手機也不例外。多核心處理器使手機擁有同時執(zhí)行多個任務(wù)的能力。但并非硬件上擁有高速多核心處理器,手機就能發(fā)揮高性能,還必須有軟件相應(yīng)支持,多處理器的性能瓶頸在軟件上[1]。
多核環(huán)境下軟件開發(fā)的核心是多線程開發(fā)。多線程技術(shù)不僅要求軟件實現(xiàn)多線程,在硬件上也必須采用多線程技術(shù)。只有與多核CPU相適應(yīng)的軟件,才能真正發(fā)揮多核的性能。iOS平臺對多線程技術(shù)提供了很好的支持,大多基于ios平臺開發(fā)的app都使用了多線程技術(shù),下面將對ios開發(fā)中多線程技術(shù)的應(yīng)用進行介紹。
2 多線程介紹(Introduction to multi-thread)
2.1 線程和多線程
要了解什么是線程,首先要知道進程的概念。手機上每一個應(yīng)用在運行前都會進駐內(nèi)存,進入內(nèi)存并且運行中的應(yīng)用程序就可稱為一個進程。一個進程可以包含一個或多個線程??梢哉J為線程是進程在調(diào)度過程中能獨立執(zhí)行某項任務(wù)的最小單位。
多線程是能使多個線程并發(fā)執(zhí)行的技術(shù)。應(yīng)用程序在手機上運行時,經(jīng)常會同時處理多項任務(wù)。在手機應(yīng)用開發(fā)中,為了實現(xiàn)多個任務(wù)并發(fā)執(zhí)行,就會采用多線程技術(shù)。一個線程同一時間只能處理一個任務(wù),為了能同時執(zhí)行多項任務(wù),程序員編程過程中就會開辟多個線程,以此來提高資源的利用率,這就是多線程編程。對于多核處理器,可以調(diào)用多個處理器核心同時運行多個線程,從而達到同步完成多項任務(wù)。
2.2 多線程的優(yōu)缺點
多線程開發(fā)技術(shù)是從單線程和多進程技術(shù)發(fā)展而來,相對于單線程和多進程而言,多線程開發(fā)有其優(yōu)勢,主要表現(xiàn)在以下幾點。首先,單線程的任務(wù)只能一個個按順序執(zhí)行,因此,如果某個任務(wù)運行時間太長,就會影響到手機處理器的使用效率。而多線程技術(shù)可以實現(xiàn)多個任務(wù)同時處理,在多核處理器時代,處理器多個核心同時運行多個線程,處理多個任務(wù),能大大提高資源的利用率。其次,相對于進程而言,線程可以利用其所在進程的資源,系統(tǒng)不需要單獨為線程分配系統(tǒng)資源,對線程的調(diào)度所付出的開銷比進程要小得多。同時能充分發(fā)揮多核心處理器的優(yōu)勢,真正實現(xiàn)多任務(wù)并發(fā)。而且,當硬件處理器數(shù)量增加時,不需要做任何更改,程序運行速度會相應(yīng)變快。
多線程技術(shù)運用得當能給應(yīng)用開發(fā)帶來極大的好處,但其也存在一些缺點。開啟的線程過多,處理器調(diào)度線程所耗資源就越大,程序設(shè)計也會更復雜。同時,開啟線程會占用一定的內(nèi)存空間,如果開啟的線程數(shù)量過多,必將占用較大的內(nèi)存空間,影響程序的性能。
3 iOS應(yīng)用開發(fā)中的多線程技術(shù)(Multi-thread technology in iOS application development)
3.1 iOS中的多線程運行機制
iOS平臺對多線程技術(shù)提供了很好的支持,在iOS應(yīng)用開發(fā)中靈活運用多線程技術(shù)可以對手機應(yīng)用進行很好的優(yōu)化。對于手機而言,大多數(shù)app都是c/s模式,都會涉及UI和業(yè)務(wù)邏輯的并行運行問題。通常,進程在創(chuàng)建時都會同時運行一個線程,這個線程被稱為主線程。iOS進程如果開啟了多個線程,系統(tǒng)會以時間片輪轉(zhuǎn)的方式,讓各個線程輪流使用處理器資源,因為時間片的時間非常短,用戶感覺象是同時在進行。最新的蘋果X采用的六核心處理器,如果同時運行多個線程,iOS可以將多個線程運行在多個核心上,實現(xiàn)真正的并發(fā)執(zhí)行,提高處理器的效率,最終達到提高應(yīng)用效率的目的。
iOS開發(fā)中,處理與UI相關(guān)的操作都會運行在主線程中,如顯示或刷新界面,處理UI事件(點擊、滾動、拖拽等)[2]。對于耗時的操作都會放在子線程,比如從網(wǎng)上獲取數(shù)據(jù),因為網(wǎng)速、數(shù)據(jù)大小或者網(wǎng)絡(luò)連接狀態(tài)等因素都會影響到所需時間,這種操作放在主線程必然會造成主線程阻塞,影響應(yīng)用運行的流暢度,給用戶不好的體驗。耗時操作放在子線程,則可以提高應(yīng)用運行效率。
3.2 iOS平臺主要的線程技術(shù)介紹[3]
iOS支持四種多線程技術(shù)Pthread、GCD、NSThread、NSOperation,各技術(shù)的特點如圖1所示。
其中前面兩種以C語言為基礎(chǔ),通過C語言來操作,對于不熟悉C語言編程的開發(fā)者而言,使用起來較困難。Pthread技術(shù)因為使用難度大,目前基本已經(jīng)過時不用[3]。而后兩種則基于objective-c,通過objective-c類的方式來執(zhí)行多線程操作。這兩種方式以objective-c類的方式對線程進行管理和操作,操作面向?qū)ο?,因此簡單易用。但NSThead技術(shù)需要開發(fā)者自行管理線程生命周期及線程同步。當多個線程同時訪問同一份資源時會造成數(shù)據(jù)錯亂,必須使用同步鎖來解決這個問題,會增加CPU資源的損耗。NSOperation技術(shù)基于GCD,并且多了一些簡單易用的功能,但是因為它是對GCD的第二次封裝,性能上比GCD略低[4]。NSOperation是基于objective-c語言的多線程技術(shù),并且是在GCD基礎(chǔ)上發(fā)展而來,只要有了objective-c語言基礎(chǔ),掌握了GCD技術(shù)的原理,就很容易上手NSOpertion技術(shù)。因此,下面將主要介紹GCD技術(shù)及其在開發(fā)中的應(yīng)用。
4 GCD多線程開發(fā)技術(shù)(GCD multi-thread development technology)
GCD是為多核的并行運算而設(shè)計的純C語言開發(fā)的一種技術(shù),提供了很多的功能強大的函數(shù),利用這些可以創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程,開發(fā)者只需告訴GCD要執(zhí)行的任務(wù),不需要編寫線程管理代碼。在進行多線程開發(fā)時可以傳Block也可以傳c函數(shù)指針[5]。在進行多核編程時方便高效,它可以自動利用更多的處理器核心進行工作。
4.1 隊列和任務(wù)
要了解GCD的工作原理,首先要知道GCD的兩個核心概念—隊列和任務(wù)。實現(xiàn)多線程要做的兩件事就是創(chuàng)建隊列和提交任務(wù)。
隊列(Dispatch Queue)是用來存放任務(wù)的集合并負責管理任務(wù)。隊列的機制就是將任務(wù)拆分成多個工作單元,將其加入到隊列中,系統(tǒng)會負責管理隊列,并將隊列放到多個線程上執(zhí)行,開發(fā)者無需管理線程。
開發(fā)者可以使用系統(tǒng)提供的函數(shù)來創(chuàng)建隊列,隊列通過一個線程池來處理隊列管理的任務(wù)。GCD的隊列是嚴格遵守先進先出(FIFO)的原則,添加到隊列的工作單元就是按照添加的循序啟動。
根據(jù)任務(wù)執(zhí)行方式的不同,隊列可以分為串行隊列和并行隊列。串行隊列的線程池中只有一個線程,隊列中的任務(wù)按順序一個個執(zhí)行,每次只能執(zhí)行一個任務(wù)。并發(fā)隊列的線程池中有多個線程,因此并發(fā)隊列的任務(wù)可以按先進新出的順序并發(fā)啟動,同時執(zhí)行多個任務(wù)。
任務(wù)就是要執(zhí)行的操作或?qū)崿F(xiàn)的功能,任務(wù)通常通過同步函數(shù)和異步函數(shù)兩種方式來執(zhí)行。他們之間的區(qū)別在于只在當前線程中執(zhí)行任務(wù),不會開啟新線程。而且,任務(wù)執(zhí)行過程中,會阻塞當前線程。只有當前線程代碼塊任務(wù)執(zhí)行結(jié)束,線程才會繼續(xù)執(zhí)行。而異步是在開辟新線程來執(zhí)行任務(wù),因此對當前線程不會有影響。
4.2 隊列的創(chuàng)建
iOS創(chuàng)建隊列大致可以分成三種類型:獲取全局并發(fā)隊列、創(chuàng)建串行和并行隊列、獲取主隊列[6]。開發(fā)者可以通過GCD提供的函數(shù)來創(chuàng)建和訪問隊列。下面將分別介紹這三種情況。
獲取全局并發(fā)隊列可以通過函數(shù)dispatch_get_global_queue(long identifier, unsigned long flags)來實現(xiàn),該函數(shù)會返回一個dispatch_queue_t類型的對象,開發(fā)者只需通過該函數(shù)就可以獲取系統(tǒng)給應(yīng)用提供的多個并發(fā)的隊列,這些隊列在當前應(yīng)用內(nèi)全局共享。函數(shù)的第一個參數(shù)用于指定隊列的優(yōu)先級,查看官方文檔可以得知優(yōu)先級參數(shù)值。第二個參數(shù)是供以后使用的,通常傳入0即可
創(chuàng)建串行和并行隊列用函數(shù)dispatch_queue_create(const char*_Nullable label,dispatch_queue_attr_t_Nullable attr)來實現(xiàn),同樣,該函數(shù)也會返回一個dispatch_queue_t類型的對象。如果應(yīng)用程序的任務(wù)需要按特定順序執(zhí)行,則創(chuàng)建串行隊列。如果需要并發(fā)執(zhí)行多個任務(wù),可以通過獲取全局并發(fā)隊列。
創(chuàng)建串行或并發(fā)隊列的函數(shù)中包含兩個參數(shù),第一個參數(shù)可以為隊列設(shè)置一個字符串,也可以為NULL。第二個參數(shù)用來指定當前函數(shù)是創(chuàng)建串行隊列還是并行隊列,默認值為NULL,表示串行。
主隊列是GCD中一個特殊的串行隊列,開發(fā)者可以通過函數(shù)dispatch_get_main_queue(void)來獲取主隊列。應(yīng)用中只要是提交給主隊列的任務(wù)都會放在主線程中執(zhí)行。
4.3 提交任務(wù)
創(chuàng)建完隊列以后,需要將任務(wù)提交給隊列。提交任務(wù)的方式分同步和異步兩種。
開發(fā)者可以兩個函數(shù)dispatch_sync(dispatch_queue_t queue,DISPATCH_NOESCAPE dispatch_block_t block)和dispatch_sync_f(dispatch_queue_t queue,void*_Nullable context,dispatch_function_t work)來實現(xiàn)同步執(zhí)行任務(wù)。這兩個函數(shù)的區(qū)別在于前者是將要執(zhí)行的任務(wù)以代碼塊的形式提交給指定的隊列,后者是將要執(zhí)行的任務(wù)以函數(shù)的方式提交給指定的隊列。
在開發(fā)過程中,可以任意選擇上述兩個函數(shù)來實現(xiàn)任務(wù)同步。同步過程由GCD來處理,開發(fā)者只需將要執(zhí)行的任務(wù)寫入代碼塊或者要調(diào)用的函數(shù)中。與同步方式一樣,異步執(zhí)行任務(wù)也對應(yīng)由兩個函數(shù)來實現(xiàn)。分別是:dispatch_async(dispatch_queue_t queue,dispatch_block_t block),dispatch_async_f(dispatch_queue_t queue,void*_Nullable context,dispatch_function_t work)。兩個函數(shù)的參數(shù)涵義和同步函數(shù)完全一致。前者是將代碼塊以異步的方式提交給指定的隊列,后者是將函數(shù)以異步的方式提交給指定的隊列。
4.4 隊列和任務(wù)的組合
在開發(fā)過程中,可以根據(jù)實際情況,將不同的隊列和不同的任務(wù)提交方式進行組合,包括串行同步、串行異步、并行同步、并行異步。采用同步還是異步?jīng)Q定是否需要開啟新的線程,而任務(wù)的執(zhí)行方式則取決于采用串行還是并行。不同的組合方式將會產(chǎn)生不同的執(zhí)行結(jié)果。
需要注意的是使用同步函數(shù)在當前串行隊列中添加任務(wù),會導致當前線程阻塞。而應(yīng)用程序的主線程必須采取異步執(zhí)行方式,這樣才可以及時相應(yīng)用戶事件。
5 GCD多線程開發(fā)實踐(GCD multi-thread development practice)
ios編程的工作原理示意圖如圖2所示,從圖可以看出,手機端應(yīng)用需要通過網(wǎng)絡(luò)向服務(wù)器發(fā)送請求獲取所需數(shù)據(jù),這些數(shù)據(jù)可能包括圖像、視頻、文字等。獲取到數(shù)據(jù)后再在UI上顯示出來。這個請求過程所耗時間取決于網(wǎng)絡(luò)服務(wù)和數(shù)據(jù)大小,在多線程編程中,網(wǎng)絡(luò)數(shù)據(jù)請求通常會作為耗時操作而開啟子線程。而UI顯示操作,iOS要求必須在主線程[7]。
如果同一個界面需要通過網(wǎng)絡(luò)請求從服務(wù)器獲取多組數(shù)據(jù),則可以通過GCD隊列組來實現(xiàn)。首先使用異步執(zhí)行多個數(shù)據(jù)請求操作,并在異步操作都執(zhí)行完畢后,回到主線程執(zhí)行操作??梢詫⒍鄠€block組成一個對列組,用于監(jiān)聽所有數(shù)據(jù)請求是否完畢,如果所有數(shù)據(jù)請求已完成則轉(zhuǎn)到主線程執(zhí)行UI操作(圖3)。代碼如下。
-(void)dispatch_group_requestData
{ //調(diào)度組
dispatch_group_t group=dispatch_group_create();
dispatch_group_async(group,dispatch_get_global_queue(0,0),^{
// 異步請求數(shù)據(jù)1代碼 });
dispatch_group_async(group,dispatch_get_global_queue(0,0),^{
// 異步請求數(shù)據(jù)2代碼});
dispatch_group_async(group,dispatch_get_global_queue(0,0),^{
// 異步請求數(shù)據(jù)3代碼});
dispatch_group_notify(group,dispatch_get_main_queue(),^{
// 數(shù)據(jù)請求完成,設(shè)置UI });}
6 結(jié)論(Conclusion)
隨著手機性能不斷增強,在ios應(yīng)用開發(fā)過程中,必須注重對軟件性能的優(yōu)化,以便充分利用硬件資源,做到App運行整體性能的提升,給用戶更好的體驗。多線程開發(fā)技術(shù)提高了CPU的利用效率,但是線程的切換增加了系統(tǒng)開銷,同時也面臨著線程不安全、線程死鎖的缺點[8]。因此,在ios開發(fā)中可以根據(jù)實際情況選擇合適的多線程技術(shù),但能單線程能處理的問題盡量不要使用多線程。
參考文獻(References)
[1] 眭俊華,劉慧娜,王建鑫,等.多核多線程技術(shù)綜述[J].計算機應(yīng)用,2013(33)(S1):239-242;261.
[2] 孔翔鳴.Swift多線程編程[J].電腦知識與技術(shù),2017(02):24-25.
[3] 傳智播客高教產(chǎn)品研發(fā)部.ios開發(fā)項目化經(jīng)典教程[M].北京:人民郵電出版社出版社,2016:1-5.
[4] 陳偉,卜慶凱.iOS系統(tǒng)中多線程技術(shù)的研究[J].電腦知識與技術(shù),2017(8):78-80.
[5] 潘小龍.IOS系統(tǒng)中不同多線程技術(shù)的研究和比較[J].中國新通信,2014,16(24):21-22.
[6] 李嵐.iOS并發(fā)程序設(shè)計中幾種方法的特點及使用技巧研究[J].電腦知識與技術(shù),2016(9):83-84.
[7] 孫翠改.Android環(huán)境下主UI線程與子線程通信機制研究[J].數(shù)字技術(shù)與應(yīng)用,2016(9):66-67.
[8] 崔政,段利國.基于Java synchronized同步鎖實現(xiàn)線程交互[J].軟件工程,2018(2):1-3.
作者簡介:
黃瀏展(1975-),男,碩士,副教授.研究領(lǐng)域:移動應(yīng)用開發(fā).