轉(zhuǎn)自Xcode Dev 并發(fā)編程之Operation Queue 隨著移動設(shè)備的更新?lián)Q代,,移動設(shè)備的性能也不斷提高,,現(xiàn)在流行的CPU已經(jīng)進入雙核、甚至四核時代,。如何充分發(fā)揮這些CPU的性能,,會變得越來越重要。在iOS中如果想要充分利用多核心CPU的優(yōu)勢,,就要采用并發(fā)編程,,提高CPU的利用率。iOS中并發(fā)編程中主要有2種方式Operation Queue和GCD(Grand Central Dispatch),。下面就來先來說一下Operation Queue,。
異步調(diào)用和并發(fā) 在深入之前,,首先說說異步調(diào)用和并發(fā),。這兩個概念在并發(fā)編程中很容易弄混淆。異步調(diào)用是指調(diào)用時無需等待結(jié)果返回的調(diào)用,,異步調(diào)用往往會觸發(fā)后臺線程處理,,比如NSURLConnection的異步網(wǎng)絡(luò)回調(diào)。并發(fā)是指多個任務(wù)(線程)同時執(zhí)行,。在異步調(diào)用的實現(xiàn)中往往采用并發(fā)機制,,然而并不是所有異步都是并發(fā)機制,也有可能是其他機制,,比如一些依靠中斷進行的操作,。
為什么Operation Queue Operation Queue提供一個面向?qū)ο蟮牟l(fā)編程接口,支持并發(fā)數(shù),,線程優(yōu)先級,,任務(wù)優(yōu)先級,,任務(wù)依賴關(guān)系等多種配置,可以方便滿足各種復雜的多任務(wù)處理場景,。 1.面向?qū)ο蠼涌?/p> 2.支持并發(fā)數(shù)配置 3.任務(wù)優(yōu)先級調(diào)度 4.任務(wù)依賴關(guān)系 5.線程優(yōu)先級配置
NSOperation簡介 iOS并發(fā)編程中,,把每個并發(fā)任務(wù)定義為一個Operation,對應(yīng)的類名是NSOperation,。NSOperation是一個抽象類,,無法直接使用,它只定義了Operation的一些基本方法,。我們需要創(chuàng)建一個繼承于它的子類或者使用系統(tǒng)預(yù)定義的子類,。目前系統(tǒng)預(yù)定義了兩個子類:NSInvocationOperation和NSBlockOperation。
NSInvocationOperation NSInvoationOperation是一個基于對象和selector的Operation,,使用這個你只需要指定對象以及任務(wù)的selector,,如果必要,你還可以設(shè)定傳遞的對象參數(shù),。
同時當這個Operation完成后,,你還可以獲取Operation中Invation執(zhí)行后返回的結(jié)果對象。
NSBlockOperation 在一個Block中執(zhí)行一個任務(wù),,這時我們就需要用到NSBlockOperation,。可以通過blockOperationWithBlock:方法來方便地創(chuàng)建一個NSBlockOperation:
運行一個Operation 調(diào)用Operation的start方法就可以直接運行一個Operation,。
start方法用來啟動一個Operation任務(wù),。同時,Operation提供一個main方法,,你的所有任務(wù)都應(yīng)該在main中進行處理,。默認的start方法中會先做出一些異常判斷然后直接調(diào)用main方法。如果需要自定義一個NSOperation必須重載main方法來執(zhí)行你所想要執(zhí)行的任務(wù),。
取消一個Operation 要取消一個Operation,,要向Operation對象發(fā)送cancel消息:
當向一個Operation對象發(fā)送cancel消息后,并不保證這個Operation對象一定能立刻取消,,這取決于你的main中對cancel的處理,。如果你在main方法中沒有對cancel進行任何處理的話,發(fā)送cancel消息是沒有任何效果的,。為了讓Operation響應(yīng)cancel消息,,那么你就要在main方法中一些適當?shù)牡胤绞謩拥呐袛鄆sCancelled屬性,如果返回YES的話,,應(yīng)釋放相關(guān)資源并立刻停止繼續(xù)執(zhí)行,。
創(chuàng)建可并發(fā)的Operation 由于默認情況下Operation的start方法中直接調(diào)用了main方法,而main方法中會有比較耗時的處理任務(wù)。如果我們在一段代碼連續(xù)start了多個Operation,,這些Operation都是阻塞地依次執(zhí)行完,,因為第二個Operation必須等到第一個Operation執(zhí)行完start內(nèi)的main并返回。Operation默認都是不可并發(fā)的(使用了Operation Queue情況下除外,,Operation Queue會獨自管理自己的線程),,因為默認情況下Operation并不額外創(chuàng)建線程。我們可以通過Operation的isConcurrent方法來判斷Operation是否是可并發(fā)的,。如果要讓Operation可并發(fā),,我們需要讓main在獨立的線程中執(zhí)行,并將isConcurrent返回YES,。
當你自定義了start或main方法時,,一定要手動的調(diào)用一些KVO通知方法,以便讓對象的KVO機制可以正常運作,。
設(shè)置Operation的completionBlock 每個Operation都可以設(shè)置一個completionBlock,,在Operation執(zhí)行完成時自動執(zhí)行這個Block。我們可以在此進行一些完成的處理,。completionBlock實現(xiàn)原理是對Operation的isFinnshed字段進行KVO(Key-Value Observing),,當監(jiān)聽到isFinnished變成YES時,就執(zhí)行completionBlock,。
設(shè)置Operation的線程優(yōu)先級 我們可以為Operation設(shè)置一個線程優(yōu)先級,,即threadPriority。那么執(zhí)行main的時候,,線程優(yōu)先級就會調(diào)整到所設(shè)置的線程優(yōu)先級,。這個默認值是0.5,我們可以在Operation執(zhí)行前修改它,。
注意:如果你重載的start方法,,那么你需要自己來配置main執(zhí)行時的線程優(yōu)先級和threadPriority字段保持一致。
Operation狀態(tài)變化 我們可以通過KVO機制來監(jiān)聽Operation的一下狀態(tài)改變,,比如一個Operation的執(zhí)行狀態(tài)或完成狀態(tài),。這些狀態(tài)的keypath包括以下幾個:
NSOperationQueue NSOperationQueue是一個Operation執(zhí)行隊列,你可以將任何你想要執(zhí)行的Operation添加到Operation Queue中,,以在隊列中執(zhí)行,。同時Operation和Operation Queue提供了很多可配置選項,。Operation Queue的實現(xiàn)中,,創(chuàng)建了一個或多個可管理的線程,為隊列中的Operation提供可高度自定的執(zhí)行環(huán)境,。
Operation的依賴關(guān)系 有時候我們對任務(wù)的執(zhí)行順序有要求,,一個任務(wù)必須在另一個任務(wù)執(zhí)行之前完成,這就需要用到Operation的依賴(Dependency)屬性。我們可以為每個Operation設(shè)定一些依賴的另外一些Operation,,那么如果依賴的Operation沒有全部執(zhí)行完畢,,這個Operation就不會被執(zhí)行。
如果將這些Operation和它所依賴的Operation加如隊列中,,那么Operation只有在它依賴的Operation都執(zhí)行完畢后才可以被執(zhí)行,。這樣我們就可以方便的控制Operation執(zhí)行順序。
Operation在隊列中執(zhí)行的優(yōu)先級 Operation在隊列中默認是按FIFO(First In First Out)順序執(zhí)行的,。同時我們可以為單個的Operation設(shè)置一個執(zhí)行的優(yōu)先級,,打亂這個順序。當Queue有空閑資源執(zhí)行新的Operation時,,會優(yōu)先執(zhí)行當前隊列中優(yōu)先級最高的待執(zhí)行Operation,。
最大并發(fā)Operation數(shù)目 在一個Operation Queue中是可以同時執(zhí)行多個Operation的,Operation Queue會動態(tài)的創(chuàng)建多個線程來完成相應(yīng)Operation,。具體的線程數(shù)是由Operation Queue來優(yōu)化配置的,,這一般取決與系統(tǒng)CPU的性能,比如CPU的核心數(shù),,和CPU的負載,。但我們還是可以設(shè)置一個最大并發(fā)數(shù)的,那么Operation Queue就不會創(chuàng)建超過最大并發(fā)數(shù)量的線程,。
如果我們將maxConcurrentOperationCount設(shè)置為1,,那么在隊列中每次只能執(zhí)行一個任務(wù)。這就是一個串行的執(zhí)行隊列了,。
Simple Code 下面我寫了一個簡單的Simple Code來說明一下Operation和Operation Queue,。
運行這段代碼,我得到了一下輸出結(jié)果:
為了更好的展示隊列優(yōu)先級效果,,我把queue的maxConcurrentOperationCount設(shè)置為1,,以便任務(wù)一個一個的執(zhí)行。從上面日志可以看出,,第一個operation1s執(zhí)行完畢后,,會執(zhí)行operation5s,而不是operation2s,,因為operation5s的queuePriority是NSOperationQueuePriorityHigh,。而第一個線程總是會第一個執(zhí)行。在看看2-4行,,我們可以看出operation1s的completionBlock比operation5s晚開始執(zhí)行,,說明它不在operation1s的線程中執(zhí)行的。正如前面所說,,completionBlock是通過KVO監(jiān)聽執(zhí)行,,一般會運行在監(jiān)聽所在線程,而不是Operation執(zhí)行的線程。
注意事項 當一個Operation被加入Queue中后,,請不要對這個Operation再進行任何修改,。因為一旦加入Queue,它隨時就有可能會被執(zhí)行,,對它的任何修改都有可能導致它的運行狀態(tài)不可控制,。
threadPriority僅僅影響了main執(zhí)行時的線程優(yōu)先級,其他的方法包括completionBlock都是以默認的優(yōu)先級來執(zhí)行的,。如果自定義的話,,也要注意在main執(zhí)行前設(shè)置好threadPriority,執(zhí)行完畢后要還原默認線程優(yōu)先級,。
經(jīng)測試,,Operation的threadPriority字段只有在Operation單獨執(zhí)行時有效,在Operation Queue中是無效的,。
第一個加入到Operation Queue中的Operation,,無論它的優(yōu)先級有多么低,總是會第一個執(zhí)行,。
|
|