iOS多線程之GCD、OperationQueue 探索開括

收錄:www.cocoachina.com/articles/90…

簡介

iOS 提供了一些技術(shù),允許您異步執(zhí)行任何任務(wù),而無需自己管理線程。異步啟動任務(wù)的技術(shù)之一是 Grand Central Dispatch (GCD)。
這種技術(shù)采用線程管理代碼,并將該代碼移動到系統(tǒng)級別。
您所要做的就是定義要執(zhí)行的任務(wù),并將它們添加到適當(dāng)?shù)姆峙申犃兄小?br> GCD 負責(zé)創(chuàng)建所需的線程,并安排任務(wù)在這些線程上運行。由于線程管理現(xiàn)在是系統(tǒng)的一部分,GCD 提供了任務(wù)管理和執(zhí)行的整體方法,比傳統(tǒng)線程提供了更高的效率。

OperationQueue(操作隊列,api 類名為 NSOperationQueue )是 Objective-C 對象,是對 GCD 的封裝。其作用非常類似于分派隊列。
您定義要執(zhí)行的任務(wù),然后將它們添加到 OperationQueue 中, OperationQueue 處理這些任務(wù)的調(diào)度和執(zhí)行。
與 GCD 一樣, OperationQueue 為您處理所有線程管理,確保在系統(tǒng)上盡可能快速有效地執(zhí)行任務(wù)。

GCD、OperationQueue 對比

核心理念

  • GCD的核心概念:
    • 將 任務(wù)(block) 添加到隊列,并且指定執(zhí)行任務(wù)的函數(shù)。
  • NSOperation 的核心概念:
    • 把 操作(異步) 添加到 隊列。

區(qū)別

  • GCD:

    • 將任務(wù)(block)添加到隊列(串行/并發(fā)/主隊列),并且指定任務(wù)執(zhí)行的函數(shù)(同步/異步)
    • GCD是底層的C語言構(gòu)成的API
    • iOS 4.0 推出的,針對多核處理器的并發(fā)技術(shù)
    • 在隊列中執(zhí)行的是由 block 構(gòu)成的任務(wù),這是一個輕量級的數(shù)據(jù)結(jié)構(gòu)
    • 要停止已經(jīng)加入 queue 的 block 需要寫復(fù)雜的代碼
    • 需要通過 Barrier(dispatch_barrier_async)或者同步任務(wù)設(shè)置任務(wù)之間的依賴關(guān)系
    • 只能設(shè)置隊列的優(yōu)先級
    • 高級功能:
      • dispatch_once_t(一次性執(zhí)行, 多線程安全);
      • dispatch_after(延遲); dispatch_group(調(diào)度組); dispatch_semaphore(信號量);
      • dispatch_apply(優(yōu)化順序不敏感大體量for循環(huán));
  • OperationQueue:

    • OC 框架,更加面向?qū)ο螅菍?GCD 的封裝。

    • iOS 2.0 推出的,蘋果推出 GCD 之后,對 NSOperation 的底層進行了全部重寫。

    • 可以設(shè)置隊列中每一個操作的 QOS() 隊列的整體 QOS

    • 操作相關(guān) Operation作為一個對象,為我們提供了更多的選擇:

      • 任務(wù)依賴(addDependency),可以跨隊列設(shè)置操作的依賴關(guān)系;
      • 在隊列中的優(yōu)先級(queuePriority) 服務(wù)質(zhì)量(qualityOfService, iOS8+);
      • 完成回調(diào)(void (^completionBlock)(void)
    • 隊列相關(guān) 服務(wù)質(zhì)量(qualityOfService, iOS8+);

    • 最大并發(fā)操作數(shù)(maxConcurrentOperationCount),GCD 不易實現(xiàn); 暫停/繼續(xù)(suspended);

    • 取消所有操作(cancelAllOperations);

    • KVO 監(jiān)聽隊列任務(wù)執(zhí)行進度(progress, iOS13+);

GCD

從GCD常見面試題到底層源碼分析,帶你深入了解不一樣的GCD
1、GCD常見面試題分析
2、GCD的注意事項(死鎖)
3、GCD的底層原理(隊列分析)

觀看點擊▼

iOS多線程-『GCD』

隊列

串行隊列(Serial Queues)

串行隊列中的任務(wù)按順序執(zhí)行;
但是不同串行隊列間沒有任何約束;
多個串行隊列同時執(zhí)行時,不同隊列中任務(wù)執(zhí)行是并發(fā)的效果。

比如:火車站買票可以有多個賣票口,但是每個排的隊都是串行隊列,整體并發(fā),單線串行。

注意防坑:串行隊列創(chuàng)建的位置。

比如下面代碼示例中:

  • 在for循環(huán)內(nèi)部創(chuàng)建時,每個循環(huán)都是創(chuàng)建一個新的串行隊列,里面只裝一個任務(wù),多個串行隊列,結(jié)果整體上是并發(fā)的效果。
    想要串行效果,必須在for循環(huán)外部創(chuàng)建串行隊列。

串行隊列適合管理共享資源。保證了順序訪問,杜絕了資源競爭。

代碼示例:

    private func serialExcuteByGCD(){
        let lArr : [UIImageView] = [imageView1, imageView2, imageView3, imageView4]

        //串行隊列,異步執(zhí)行時,只開一個子線程
        let serialQ = DispatchQueue.init(label: "com.companyName.serial.downImage")

        for i in 0..<lArr.count{
            let lImgV = lArr[i]

            //清空舊圖片
            lImgV.image = nil

         //注意,防坑:串行隊列創(chuàng)建的位置,在這創(chuàng)建時,每個循環(huán)都是一個新的串行隊列,里面只裝一個任務(wù),多個串行隊列,整體上是并行的效果。
            //            let serialQ = DispatchQueue.init(label: "com.companyName.serial.downImage")

            serialQ.async {

                print("第\(i)個 開始,%@",Thread.current)
                Downloader.downloadImageWithURLStr(urlStr: imageURLs[i]) { (img) in
                    let lImgV = lArr[i]

                    print("第\(i)個 結(jié)束")
                    DispatchQueue.main.async {
                        print("第\(i)個 切到主線程更新圖片")
                        lImgV.image = img
                    }
                    if nil == img{
                        print("第\(i+1)個img is nil")
                    }
                }
            }
        }
    }

并發(fā)隊列(Concurrent Queues)

并發(fā)隊列依舊保證中任務(wù)按加入的先后順序開始(FIFO),但是無法知道執(zhí)行順序,執(zhí)行時長和某一時刻的任務(wù)數(shù)。按 FIFO 開始后,他們之間不會相互等待。

比如:提交了 #1,#2,#3 任務(wù)到并發(fā)隊列,開始的順序是 #1,#2,#3。#2 和 #3 雖然開始的比 #1 晚,但是可能比 #1 執(zhí)行結(jié)束的還要早。任務(wù)的執(zhí)行是由系統(tǒng)決定的,所以執(zhí)行時長和結(jié)束時間都無法確定。

需要用到并發(fā)隊列時,強烈建議 使用系統(tǒng)自帶的四種全局隊列之一。但是,當(dāng)你需要使用 barrier 對隊列中任務(wù)進行柵欄時,只能使用自定義并發(fā)隊列。

Use a barrier to synchronize the execution of one or more tasks in your dispatch queue. When you add a barrier to a concurrent dispatch queue, the queue delays the execution of the barrier block (and any tasks submitted after the barrier) until all previously submitted tasks finish executing. After the previous tasks finish executing, the queue executes the barrier block by itself. Once the barrier block finishes, the queue resumes its normal execution behavior.

對比:barrier 和鎖的區(qū)別

  • 依賴對象不同,barrier 依賴的對象是自定義并發(fā)隊列,鎖操作依賴的對象是線程。
  • 作用不同,barrier 起到自定義并發(fā)隊列中柵欄的作用;鎖起到多線程操作時防止資源競爭的作用。

代碼示例:

private func concurrentExcuteByGCD(){
        let lArr : [UIImageView] = [imageView1, imageView2, imageView3, imageView4]

        for i in 0..<lArr.count{
            let lImgV = lArr[i]

            //清空舊圖片
            lImgV.image = nil

            //并行隊列:圖片下載任務(wù)按順序開始,但是是并行執(zhí)行,不會相互等待,任務(wù)結(jié)束和圖片顯示順序是無序的,多個子線程同時執(zhí)行,性能更佳。
            let lConQ = DispatchQueue.init(label: "cusQueue", qos: .background, attributes: .concurrent)
            lConQ.async {
                print("第\(i)個開始,%@", Thread.current)
                Downloader.downloadImageWithURLStr(urlStr: imageURLs[i]) { (img) in
                    let lImgV = lArr[i]
                      print("第\(i)個結(jié)束")
                    DispatchQueue.main.async {
                        lImgV.image = img
                    }
                    if nil == img{
                        print("第\(i+1)個img is nil")
                    }
                }
            }
        }
    }

串行、并發(fā)隊列對比圖

注意事項

  • 無論串行還是并發(fā)隊列,都是 FIFO ;
    一般創(chuàng)建 任務(wù)(blocks)和加任務(wù)到隊列是在主線程,但是任務(wù)執(zhí)行一般是在其他線程(asyc)。
    需要刷新 UI 時,如果當(dāng)前不再主線程,需要切回主線程執(zhí)行。
    當(dāng)不確定當(dāng)前線程是否在主線程時,可以使用下面代碼:

    /**
    Submits a block for asynchronous execution on a main queue and returns immediately.
    */
    static inline void dispatch_async_on_main_queue(void (^block)()) {
    if (NSThread.isMainThread) {
        block();
    } else {
        dispatch_async(dispatch_get_main_queue(), block);
    }
    }
    
  • 主隊列是串行隊列,每個時間點只能有一個任務(wù)執(zhí)行,因此如果耗時操作放到主隊列,會導(dǎo)致界面卡頓。

  • 系統(tǒng)提供一個串行主隊列,<u style="box-sizing: border-box;">4個</u> 不同優(yōu)先級的全局隊列。 用 dispatch_get_global_queue 方法獲取全局隊列時,第一個參數(shù)有 4 種類型可選:

    • DISPATCH_QUEUE_PRIORITY_HIGH
    • DISPATCH_QUEUE_PRIORITY_DEFAULT
    • DISPATCH_QUEUE_PRIORITY_LOW
    • DISPATCH_QUEUE_PRIORITY_BACKGROUND
  • 串行隊列異步執(zhí)行時,切到主線程刷 UI 也需要時間,切換完成之前,指令可能已經(jīng)執(zhí)行到下個循環(huán)了。但是看起來圖片還是依次下載完成和顯示的,因為每一張圖切到主線程顯示都需要時間。詳見 demo 示例。

  • iOS8 之后,如果需要添加可被取消的任務(wù),可以使用 DispatchWorkItem 類,此類有 cancel 方法。

  • 應(yīng)該避免創(chuàng)建大量的串行隊列,如果希望并發(fā)執(zhí)行大量任務(wù),請將它們提交給全局并發(fā)隊列之一。創(chuàng)建串行隊列時,請嘗試為每個隊列確定一個用途,例如保護資源或同步應(yīng)用程序的某些關(guān)鍵行為(如藍牙檢測結(jié)果需要有序處理的邏輯)。

block(塊)相關(guān)

堆棧到底有什么區(qū)別?為什么面試中經(jīng)常問到?

  • 堆的Block能捕獲變量,棧的Block是否也行?
  • 棧的Block堆上的使用有什么區(qū)別?
  • __weak真的能解決循環(huán)引用嗎?
  • 如何理解Block底層的結(jié)構(gòu)體?

觀看點擊▼

Block底層原理與LLDB Plugin

調(diào)度隊列復(fù)制添加到它們中的塊,并在執(zhí)行完成時釋放塊。
雖然隊列在執(zhí)行小任務(wù)時比原始線程更有效,但是創(chuàng)建塊并在隊列上執(zhí)行它們?nèi)匀淮嬖陂_銷。
如果一個塊執(zhí)行的工作量太少,那么內(nèi)聯(lián)執(zhí)行它可能比將它分派到隊列中要便宜得多。
判斷一個塊是否工作量太少的方法是使用性能工具為每個路徑收集度量數(shù)據(jù)并進行比較。
您可能希望將 block 的部分代碼包含在 @autoreleasepool 中,以處理這些對象的內(nèi)存管理。
盡管 GCD 調(diào)度隊列擁有自己的自動釋放池,但它們不能保證這些池何時耗盡。
如果您的應(yīng)用程序是內(nèi)存受限的,那么創(chuàng)建您自己的自動釋放池可以讓您以更有規(guī)律的間隔釋放自動釋放對象的內(nèi)存。

dispatch_after

dispatch_after 函數(shù)并不是在指定時間之后才開始執(zhí)行處理,而是在指定時間之后將任務(wù)追加到隊列中。
這個時間并不是絕對準(zhǔn)確的。

代碼示例:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"2s后執(zhí)行");
    });

dispatch_semaphore

在多線程訪問可變變量時,是非線程安全的。
可能導(dǎo)致程序崩潰。
此時,可以通過使用信號量(semaphore)技術(shù),保證多線程處理某段代碼時,后面線程等待前面線程執(zhí)行,保證了多線程的安全性。
使用方法記兩個就行了,一個是wait(dispatch_semaphore_wait),一個是signal(dispatch_semaphore_signal)。

dispatch_apply

當(dāng)每次迭代中執(zhí)行工作與其他所有迭代中執(zhí)行的工作不同,且每個循環(huán)完成的順序不重要時,可以用 dispatch_apply 函數(shù)替換循環(huán)。

注意:替換后, dispatch_apply 函數(shù)整體上是同步執(zhí)行,內(nèi)部 block 的執(zhí)行類型(串行/并發(fā))由隊列類型決定,但是串行隊列易死鎖,建議用并發(fā)隊列。

原循環(huán):

for (i = 0; i < count; i++) {
   printf("%u\n",i);
}
printf("done");

優(yōu)化后:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

 //count 是迭代的總次數(shù)。
dispatch_apply(count, queue, ^(size_t i) {
   printf("%u\n",i);
});

//同樣在上面循環(huán)結(jié)束后才調(diào)用。
printf("done");

您應(yīng)該確保您的任務(wù)代碼在每次迭代中完成合理數(shù)量的工作。
與您分派到隊列的任何塊或函數(shù)一樣,調(diào)度該代碼以便執(zhí)行會帶來開銷。
如果循環(huán)的每次迭代只執(zhí)行少量的工作,那么調(diào)度代碼的開銷可能會超過將代碼分派到隊列可能帶來的性能優(yōu)勢。
如果您在測試期間發(fā)現(xiàn)這一點是正確的,那么您可以使用步進來增加每個循環(huán)迭代期間執(zhí)行的工作量。
通過大步前進,您可以將原始循環(huán)的多個迭代集中到一個塊中,并按比例減少迭代次數(shù)。
例如,如果您最初執(zhí)行了 100次 迭代,但決定使用步長為 4 的迭代,那么您現(xiàn)在從每個塊執(zhí)行 4 次循環(huán)迭代,迭代次數(shù)為 25次 。

自問自答

  • 一個隊列的不同任務(wù)可以在多個線程執(zhí)行嗎?

    • 串行隊列,異步執(zhí)行時,只開一個子線程;
    • 無所謂多個線程執(zhí)行;
    • 并發(fā)隊列,異步執(zhí)行時,會自動開多個線程,可以在多個線程并發(fā)執(zhí)行不同的任務(wù)。
  • 一個線程可以同時執(zhí)行多個隊列的任務(wù)嗎?

    • 一個線程某個時間點只能執(zhí)行一個任務(wù),執(zhí)行完畢后,可能執(zhí)行到來自其他隊列的任務(wù)(如果有的話)。
      比如:主線程除了執(zhí)行主隊列中任務(wù)外,也可能會執(zhí)行非主隊列中的任務(wù)。

隊列與線程關(guān)系示例圖:
  • qualityOfService 和 queuePriority 的區(qū)別是什么?

    • qualityOfService:
      • 用于表示 operation 在獲取系統(tǒng)資源時的優(yōu)先級,默認值:NSQualityOfServiceBackground,我們可以根據(jù)需要給 operation 賦不同的優(yōu)化級,如最高優(yōu)化級:NSQualityOfServiceUserInteractive。
    • queuePriority:
      • 用于設(shè)置 operation 在 operationQueue 中的相對優(yōu)化級,同一 queue 中優(yōu)化級高的 operation(isReady 為 YES) 會被優(yōu)先執(zhí)行。
        需要注意區(qū)分 qualityOfService (在系統(tǒng)層面,operation 與其他線程獲取資源的優(yōu)先級) 與 queuePriority (同一 queue 中 operation 間執(zhí)行的優(yōu)化級)的區(qū)別。
        同時,需要注意 dependencies (嚴格控制執(zhí)行順序)與 queuePriority (queue 內(nèi)部相對優(yōu)先級)的區(qū)別。
  • 添加依賴后,隊列中網(wǎng)絡(luò)請求任務(wù)有依賴關(guān)系時,任務(wù)結(jié)束判定以數(shù)據(jù)返回為準(zhǔn)還是以發(fā)起請求為準(zhǔn)?

    • 以發(fā)起請求為準(zhǔn)。

OperationQueue

NSOperation NSOperation 是一個"抽象類",不能直接使用。
抽象類的用處是定義子類共有的屬性和方法。
NSOperation 是基于 GCD 做的面向?qū)ο蟮姆庋b。
相比較 GCD 使用更加簡單,并且提供了一些用 GCD 不是很好實現(xiàn)的功能。
是蘋果公司推薦使用的并發(fā)技術(shù)。

它有兩個子類:

  • NSInvocationOperation (調(diào)用操作)
  • NSBlockOperation (塊操作) 一般常用NSBlockOperation,代碼簡單,同時由于閉包性使它沒有傳參問題。任務(wù)被封裝在 NSOperation 的子類實例類對象里,一個 NSOperation 子類對象可以添加多個任務(wù) block 和 一個執(zhí)行完成 block ,當(dāng)其關(guān)聯(lián)的所有 block 執(zhí)行完時,就認為操作結(jié)束了。
  • NSOperationQueue OperationQueue也是對 GCD 的高級封裝,更加面向?qū)ο?,可以實現(xiàn) GCD 不方便實現(xiàn)的一些效果。被添加到隊列的操作默認是異步執(zhí)行的。

PS:常見的抽象類有:

  • UIGestureRecognizer

  • CAAnimation

  • CAPropertyAnimation

    可以實現(xiàn) 非FIFO 效果

通過對不同操作設(shè)置依賴,或優(yōu)先級,可實現(xiàn) 非FIFO 效果。

代碼示例:

func testDepedence(){
        let op0 = BlockOperation.init {
            print("op0")
        }

        let op1 = BlockOperation.init {
            print("op1")
        }

        let op2 = BlockOperation.init {
            print("op2")
        }

        let op3 = BlockOperation.init {
            print("op3")
        }

        let op4 = BlockOperation.init {
            print("op4")
        }

        op0.addDependency(op1)
        op1.addDependency(op2)

        op0.queuePriority = .veryHigh
        op1.queuePriority = .normal
        op2.queuePriority = .veryLow

        op3.queuePriority = .low
        op4.queuePriority = .veryHigh

        gOpeQueue.addOperations([op0, op1, op2, op3, op4], waitUntilFinished: false)
    }

說明:操作間不存在依賴時,按優(yōu)先級執(zhí)行;存在依賴時,按依賴關(guān)系先后執(zhí)行(與無依賴關(guān)系的其他任務(wù)相比,依賴集合的執(zhí)行順序不確定)

隊列暫停/繼續(xù)

通過對隊列的isSuspended屬性賦值,可實現(xiàn)隊列中未執(zhí)行任務(wù)的暫停和繼續(xù)效果。正在執(zhí)行的任務(wù)不受影響。

///暫停隊列,只對未執(zhí)行中的任務(wù)有效。本例中對串行隊列的效果明顯。并發(fā)隊列因4個任務(wù)一開始就很容易一起開始執(zhí)行,即使掛起也無法影響已處于執(zhí)行狀態(tài)的任務(wù)。
    @IBAction func pauseQueueItemDC(_ sender: Any) {
        gOpeQueue.isSuspended = true
    }

    ///恢復(fù)隊列,之前未開始執(zhí)行的任務(wù)會開始執(zhí)行
    @IBAction func resumeQueueItemDC(_ sender: Any) {
       gOpeQueue.isSuspended = false
    }

取消操作

  • 一旦添加到操作隊列中,操作對象實際上歸隊列所有,不能刪除。取消操作的唯一方法是取消它??梢酝ㄟ^調(diào)用單個操作對象的 cancel 方法來取消單個操作對象,也可以通過調(diào)用隊列對象的 cancelAllOperations 方法來取消隊列中的所有操作對象。
  • 更常見的做法是取消所有隊列操作,以響應(yīng)某些重要事件,如應(yīng)用程序退出或用戶專門請求取消,而不是有選擇地取消操作。

取消單個操作對象

取消(cancel)時,有 3 種情況:

1.操作在隊列中等待執(zhí)行,這種情況下,操作將不會被執(zhí)行。
2.操作已經(jīng)在執(zhí)行中,此時,系統(tǒng)不會強制停止這個操作,但是,其 cancelled屬性會被置為 true 。
3.操作已完成,此時,cancel 無任何影響。

取消隊列中的所有操作對象

方法: cancelAllOperations。同樣只會對未執(zhí)行的任務(wù)有效。

demo 中代碼:

    deinit {
        gOpeQueue.cancelAllOperations()
        print("die:%@",self)
    }

自問自答

  • 通過設(shè)置操作間依賴,可以實現(xiàn) 非FIFO 的指定順序效果。那么,通過設(shè)置最大并發(fā)數(shù)為 1 ,可以實現(xiàn)指定順序效果嗎?

不可以! 設(shè)置最大并發(fā)數(shù)為 1 后,雖然每個時間點只執(zhí)行一個操作,但是操作的執(zhí)行順序仍然基于其他因素,如操作的依賴關(guān)系,操作的優(yōu)先級(依賴關(guān)系比優(yōu)先級級別更高,即先根據(jù)依賴關(guān)系排序;
不存在依賴關(guān)系時,才根據(jù)優(yōu)先級排序)。
因此,序列化 操作隊列 不會提供與 GCD 中的序列 分派隊列 完全相同的行為。
如果操作對象的執(zhí)行順序?qū)δ苤匾敲茨鷳?yīng)該在將操作添加到隊列之前使用 依賴關(guān)系 建立該順序,或改用 GCD 的 串行隊列 實現(xiàn)序列化效果。

  • Operation Queue的 block 中為何無需使用 [weak self] 或 [unowned self] ?

即使隊列對象是為全局的,self -> queue -> operation block -> self,的確會造成循環(huán)引用。
但是在隊列里的操作執(zhí)行完畢時,隊列會自動釋放操作,自動解除循環(huán)引用。所以不必使用 [weak self] 或 [unowned self] 。
此外,這種循環(huán)引用在某些情況下非常有用,你無需額外持有任何對象就可以讓操作自動完成它的任務(wù)。
比如下載頁面下載過程中,退出有循環(huán)引用的界面時,如果不執(zhí)行 cancelAllOperation 方法,可以實現(xiàn)繼續(xù)執(zhí)行剩余隊列中下載任務(wù)的效果。

func addOperation(_ op: Operation) Discussion: Once added, the specified operation remains in the queue until it finishes executing. Declaration

func addOperation(_ block: @escaping () -> Void) Parameters block The block to execute from the operation. The block takes no parameters and has no return value. Discussion This method adds a single block to the receiver by first wrapping it in an operation object. You should not attempt to get a reference to the newly created operation object or determine its type information.

  • 操作的 QOS 和隊列的 QOS 有何關(guān)系?

隊列的 QOS 設(shè)置,會自動把較低優(yōu)先級的操作提升到與隊列相同優(yōu)先級。(原更高優(yōu)先級操作的優(yōu)先級保持不變)。
后續(xù)添加進隊列的操作,優(yōu)先級低于隊列優(yōu)先級時,也會被自動提升到與隊列相同的優(yōu)先級。
注意,蘋果文檔如下的解釋是錯誤的 This property specifies the service level applied to operation objects added to the queue. If the operation object has an explicit service level set, that value is used instead.

常見問題

如何解決資源競爭問題

資源競爭可能導(dǎo)致數(shù)據(jù)異常,死鎖,甚至因訪問野指針而崩潰。

  • 對于有明顯先后依賴關(guān)系的任務(wù),最佳方案是 GCD串行隊列,可以在不使用線程鎖時保證資源互斥。

  • 其他情況,對存在資源競爭的代碼加鎖或使用信號量(初始參數(shù)填1,表示只允許一條線程訪問資源)。

  • 串行隊列同步執(zhí)行時,如果有任務(wù)相互等待,會死鎖。
    比如:在主線程上同步執(zhí)行任務(wù)時,因任務(wù)和之前已加入主隊列但未執(zhí)行的任務(wù)會相互等待,導(dǎo)致死鎖。

    func testDeadLock(){
        //主隊列同步執(zhí)行,會導(dǎo)致死鎖。block需要等待testDeadLock執(zhí)行,而主隊列同步調(diào)用,又使其他任務(wù)必須等待此block執(zhí)行。于是形成了相互等待,就死鎖了。
        DispatchQueue.main.sync {
            print("main block")
        }
        print("2")
    }
    

但是下面代碼不會死鎖,故串行隊列同步執(zhí)行任務(wù)不一定死鎖。

- (void)testSynSerialQueue{
    dispatch_queue_t myCustomQueue;
    myCustomQueue = dispatch_queue_create("com.example.MyCustomQueue", NULL);

    dispatch_async(myCustomQueue, ^{
        printf("Do some work here.\n");
    });

    printf("The first block may or may not have run.\n");

    dispatch_sync(myCustomQueue, ^{
        printf("Do some more work here.\n");
    });
    printf("Both blocks have completed.\n");
}

如何提高代碼效率

“西餅傳說”

代碼設(shè)計優(yōu)先級:
系統(tǒng)方法 > 并行 > 串行 > 鎖,簡記為:<u style="box-sizing: border-box;">西餅傳說</u>

  • 盡可能依賴 系統(tǒng) 框架。實現(xiàn)并發(fā)性的最佳方法是利用系統(tǒng)框架提供的內(nèi)置并發(fā)性。
  • 盡早識別系列任務(wù),并盡可能使它們更加 并行。如果因為某個任務(wù)依賴于某個共享資源而必須連續(xù)執(zhí)行該任務(wù),請考慮更改體系結(jié)構(gòu)以刪除該共享資源。您可以考慮為每個需要資源的客戶機制作資源的副本,或者完全消除該資源。
  • 不使用鎖來保護某些共享資源,而是指定一個 串行隊列 (或使用操作對象依賴項)以正確的順序執(zhí)行任務(wù)。
  • 避免使用 。GCD 調(diào)度隊列操作隊列 提供的支持使得在大多數(shù)情況下不需要鎖定。

術(shù)語解釋摘錄

  • 異步任務(wù)(asynchronous tasks):
    • 由一個線程啟動,但實際上在另一個線程上運行,利用額外的處理器資源更快地完成工作。
  • 互斥(mutex):
    • 提供對共享資源的互斥訪問的鎖。 互斥鎖一次只能由一個線程持有。試圖獲取由不同線程持有的互斥對象會使當(dāng)前線程處于休眠狀態(tài),直到最終獲得鎖為止。
  • 進程(process):
    • 應(yīng)用軟件或程序的運行時實例。 進程有自己的虛擬內(nèi)存空間和系統(tǒng)資源(包括端口權(quán)限) ,這些資源獨立于分配給其他程序的資源。一個進程總是包含至少一個線程(主線程) ,并且可能包含任意數(shù)量的其他線程。
  • 信號量(semaphore):
    • 限制對共享資源訪問的受保護變量。 互斥(Mutexes)和條件(conditions)都是不同類型的信號量。
  • 任務(wù)(task),表示需要執(zhí)行的工作量。
  • 線程(thread):
    • 進程中的執(zhí)行流程。 每個線程都有自己的堆??臻g,但在其他方面與同一進程中的其他線程共享內(nèi)存。
  • 運行循環(huán)(run loop):
    • 一個事件處理循環(huán), 接收事件并派發(fā)到適當(dāng)?shù)奶幚沓绦颉?/li>

文末推薦:iOS熱門文集&視頻解析

① Swift

② iOS底層技術(shù)

③ iOS逆向防護

④ iOS面試合集

喜歡的小伙伴記得點贊喔~

收藏等于白嫖,點贊才是真情?( ′???` )?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容