iOS多線程之GCD

主要涉及到的概念git

  • 任務:放在block中的代碼,執行方式有 同步執行(sync)異步執行(async)
  • 隊列: 串行隊列(serial)併發隊列(concurrent)
    • 主隊列:本質是一個串行隊列
    • 全局隊列:本質是一個併發隊列

知識點:程序員

  • 主隊列的任務必定在主線程執行,主線程能夠執行非主隊列的任務
  • 同步執行不開闢子線程,在當前線程執行
  • 異步執行具備開闢子線程的能力,可是不必定會開闢,後面會有例子
  • 同步執行串行隊列按照順序串行執行
  • 同步執行併發隊列依然是按照順序串行執行
  • 異步執行串行隊列會開闢一個子線程,按順序執行隊列任務
  • 異步執行併發隊列具有開闢多個子線程的能力

任務的執行方式有兩種,隊列有四種,共有8種組合,分別爲:github

同步執行+串行隊列

dispatch_queue_t queueSerial = dispatch_queue_create("com.serial", DISPATCH_QUEUE_SERIAL);
    
    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_sync(queueSerial, task);
    dispatch_sync(queueSerial, task1);
    NSLog(@"end");
複製代碼

當前是在主線程運行,同步執行會阻塞當前線程(主線程),因此end會在執行完tasktask1以後輸出,安全

dispatch_sync(queueSerial, task);
複製代碼
  • 同步執行dispatch_sync阻塞當前線程(主線程)
  • 向串行隊列queueSerial中插入任務task
  • 在當前線程(主線程)執行task任務
  • task任務執行完成解除對當前線程(主線程)的阻塞
dispatch_sync(queueSerial, task1); //同理
複製代碼
  • 同步執行dispatch_sync阻塞當前線程(主線程)
  • 向串行隊列queueSerial中插入任務task1
  • 在當前線程(主線程)執行task1任務
  • task1任務執行完成解除對當前線程(主線程)的阻塞

因此執行順序爲task->task1->endmarkdown

image.png

同步執行+併發隊列

dispatch_queue_t queueConcurrent = dispatch_queue_create("com.conc", DISPATCH_QUEUE_CONCURRENT);

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_sync(queueConcurrent, task);
    dispatch_sync(queueConcurrent, task1);
    NSLog(@"end");
複製代碼

在當前線程同步執行不管是串行隊列仍是併發隊列任務都是按照順序執行,其結果和 同步執行+串行隊列 一致多線程

同步執行全局隊列

//第一個參數是隊列優先級,第二個參數是保留值默認傳0
    dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_sync(queueGlobal, task);
    dispatch_sync(queueGlobal, task1);
    NSLog(@"end");
複製代碼

全局隊列本質是併發隊列,其結果和 同步執行+併發隊列 一致併發

同步執行主隊列

dispatch_queue_t queueMain = dispatch_get_main_queue();

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_sync(queueMain, task); //我也是一個任務 記爲 sync1
    dispatch_sync(queueMain, task1);
    NSLog(@"end");
複製代碼

這種狀況比較特殊,會引發死鎖,其緣由是同步執行語句dispatch_sync(queueMain, task);自己也是一個任務,咱們記爲 sync1sync1任務是在當前線程(主線程)同步執行的,sync1任務是向主隊列中添加一個任務task並在當前線程(主線程)同步執行任務task,也就是說sync1任務執行完成的前提是task任務執行完成。 由於task任務是在sync1任務以後插入主隊列的,因此主線程要先執行完sync1任務纔會去執行task任務,而sync1任務執行完成的前提是task任務執行完成,因此就陷入了死鎖狀態,形成崩潰。app

image.png

相似的異步

dispatch_queue_t queueSerial = dispatch_queue_create("com.serial", DISPATCH_QUEUE_SERIAL);
    void(^task1)(void) = ^{
        NSLog(@"死鎖了嗎");
    };

    void(^task2)(void) = ^{
        dispatch_sync(queueSerial, task1);
    };

    //開闢子線程,並向串行隊列queueSerial中添加任務 task2
    dispatch_async(queueSerial, task2);
複製代碼

dispatch_async(queueSerial, task2);自己也是一個任務記爲sync1,但不一樣的是sync1是在當前線程(主線程)執行,sync1任務執行結果是向串行隊列queueSerial中添加任務task2並開闢子線程執行task2,任務task2是向串行隊列queueSerial中添加任務task1並在當前線程同步執行任務task1,也就是說task2任務執行完成的前提是task1任務執行完成,由於task2任務比task1任務先添加到串行隊列queueSerial,因此任務task2執行完成以後纔會去執行tasi1async

注意:異步執行+串行隊列只會開闢一條子線程

image.png

異步執行串行隊列

dispatch_queue_t queueSerial = dispatch_queue_create("com.serial", DISPATCH_QUEUE_SERIAL);

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_async(queueSerial, task); //我也是一個任務 記爲 sync1
    dispatch_async(queueSerial, task1);
    NSLog(@"end");
複製代碼

tasktask1分別添加到串行隊列queueSerial,開闢一條子線程順序執行隊列queueSerial中的任務,由於end是在主線程輸出的,因此輸出順序爲end->task->task1,由於異步執行+串行隊列只開闢一條子線程,因此tasktask1在同一個線程中執行

image.png

異步執行併發隊列

這個時候理論上能夠開闢多個子線程了

dispatch_queue_t queueConcurrent = dispatch_queue_create("com.conc", DISPATCH_QUEUE_CONCURRENT);

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_async(queueConcurrent, task);
    dispatch_async(queueConcurrent, task1);
    NSLog(@"end");
複製代碼

由於tasktask1有可能不是在同一個字線程執行的,兩個任務不必定誰先結束執行,因此不能肯定tasktask1誰先輸出,由於end是在主線程輸出的,因此先輸出end

異步執行主隊列

dispatch_queue_t queueMain = dispatch_get_main_queue();

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_async(queueMain, task);
    dispatch_async(queueMain, task1);
複製代碼

主隊列本質上也是一個串行隊列,因此按照順序執行task->task1,另外祝隊列任務只能在主線程執行,因此tasktask1都在主線程執行

異步執行全局隊列

dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    void(^task)(void) = ^{
        NSLog(@"task--%@",[NSThread currentThread]);
    };

    void(^task1)(void) = ^{
        NSLog(@"task1--%@",[NSThread currentThread]);
    };

    dispatch_async(queueGlobal, task);
    dispatch_async(queueGlobal, task1);
複製代碼

全局隊列是一個併發隊列,因此有能力開闢多個子線程,固然也可能在一個子線程中去執行

image.png

線程間通信

咱們須要將一些耗時任務放在子線程,執行完畢以後再回到主線程刷新頁面

dispatch_queue_t queueMain = dispatch_get_main_queue();
    dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);
    dispatch_async(queueGlobal, ^{
        sleep(3);//模擬耗時操做
        dispatch_async(queueMain, ^{
            NSLog(@"主線程");
        });

    });
複製代碼

GCD 柵欄方法:dispatch_barrier_async

在《程序員的自我修養:連接、裝載與庫。》一書的過分優化部分有這麼一段話

「CPU的亂序執行能力讓咱們對多線程的安全保障的努力變得異常困難。所以要保證線程安全,阻止CPU換序是必需的。遺憾的是,如今並不存在可移植的阻止換序的方法。一般狀況下是調用CPU提供的一條指令,這條指令經常被稱爲barrier。一條barrier指令會阻止CPU將該指令以前的指令交換到barrier以後,反之亦然。換句話說,barrier指令的做用相似於一個攔水壩,阻止換序「穿透」這個大壩。」

摘錄來自: 俞甲子 石凡 潘愛民. 「程序員的自我修養:連接、裝載與庫。」 Apple Books.

爲了保證某些操做的原子性,CUP提供了barrier指令,用來保證在barrier指令以前的指令執行完成以後纔會執行barrier以後的指令,dispatch_barrier_async的意思大致也是如此,在異步執行併發隊列中保證先執行完dispatch_barrier_async以前的任務,而後再執行dispatch_barrier_async中的任務,其次執行dispatch_barrier_async以後的任務

image.png

dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    dispatch_async(queueGlobal, ^{
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_async(queueGlobal, ^{
        sleep(2);
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_barrier_async(queueGlobal, ^{
        NSLog(@"barrier--%@",[NSThread currentThread]);
    });

    dispatch_async(queueGlobal, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_async(queueGlobal, ^{
        NSLog(@"4--%@",[NSThread currentThread]);
    });
複製代碼

全局隊列是一個併發隊列,盲猜這裏應該先打印出來12,而後執行barrier,而後隨機打出34,可是實際上呢

image.png

barrier彷佛跟dispatch_async同樣,並無起到什麼做用,查了一番資料終於在官方文檔找到正解 dispatch​_barrier​_async

image.png

若是本身經過dispatch_queue_create建立併發隊列沒問題,若是是一個串行隊列或者全局併發隊列那麼和dispatch_async效果同樣同樣的,咱們換成本身建立的併發隊列

dispatch_queue_t queueConcurrent = dispatch_queue_create("com.conc", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queueConcurrent, ^{
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_async(queueConcurrent, ^{
        sleep(2);
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_barrier_async(queueConcurrent, ^{
        NSLog(@"barrier--%@",[NSThread currentThread]);
    });

    dispatch_async(queueConcurrent, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_async(queueConcurrent, ^{
        NSLog(@"4--%@",[NSThread currentThread]);
    });
    NSLog(@"end");
複製代碼

image.png

從結果看到dispatch_barrier_async並無阻塞當前線程,而只是阻塞了當前異步隊列其餘任務的執行,官方文檔也證明了這一點

image.png

dispatch_barrier_sync

咱們先去官方文檔看一下dispatch_barrier_sync

image.png

一方面dispatch_barrier_sync會阻塞當前線程,另外一方面可能形成死鎖deadlock,咱們驗證一下

dispatch_queue_t queueConcurrent = dispatch_queue_create("com.conc", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(queueConcurrent, ^{
        sleep(2);
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_async(queueConcurrent, ^{
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_barrier_sync(queueConcurrent, ^{
        NSLog(@"barrier--%@",[NSThread currentThread]);
    });

    dispatch_async(queueConcurrent, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_async(queueConcurrent, ^{
        NSLog(@"4--%@",[NSThread currentThread]);
    });
    NSLog(@"end");
複製代碼

image.png

咱們看到dispatch_barrier_sync確實阻塞了當前線程,end是在dispatch_barrier_sync以後輸出的

dispatch_queue_t queueConcurrent = dispatch_queue_create("com.conc", DISPATCH_QUEUE_CONCURRENT);

    dispatch_barrier_sync(queueConcurrent, ^{
        NSLog(@"barrier--%@",[NSThread currentThread]);
        dispatch_sync(queueConcurrent, ^{
            NSLog(@"%@",[NSThread currentThread]);
        });
    });
複製代碼

image.png

不知道這是否是文檔中所說的Calling this function and targeting the current queue results in deadlock.反正實現了deadlock效果

另外文檔提到了Block_copy

image.png

我從源碼看到確實dispatch_barrier_async調用了_dispatch_Block_copy

image.png

image.png

dispatch_barrier_sync是沒有copy,這多是由於dispatch_barrier_sync阻塞線程後面代碼不執行,而dispatch_barrier_async沒有阻塞線程,那麼後面就可能對函數體修改????因此拷貝一份這裏留下問好

GCD 延時執行方法:dispatch_after

2秒以後將任務添加到主隊列,具體何時執行還要看CUP的調度

dispatch_queue_t mainQueue = dispatch_get_main_queue();

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0*NSEC_PER_SEC)), mainQueue, ^{
       //2秒以後將任務添加到主隊列,具體何時執行還要看CUP的調度
        NSLog(@"執行了");
    });
複製代碼

GCD 一次性代碼(只執行一次):dispatch_once

實現單例

static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
       //單例
    });
複製代碼

更多內容能夠看一下# iOS多線程之dispatch_once剖析

GCD 快速迭代方法:dispatch_apply

先來看一下文檔dispatch_apply

image.png

能夠看到dispatch_apply和可重入且安全的併發隊列能夠實現高效的遍歷操做,若是是一個串行隊列那麼就體現不出來他的高效之處了

dispatch_apply(10, queueGlobal, ^(size_t index) {
        NSLog(@"%zd--%@",index,[NSThread currentThread]);
    });
    NSLog(@"end");
複製代碼

image.png

dispatch_apply會利用多個線程來遍歷,不光是子線程,還能夠調用主線程,另外他還會阻塞當前線程

Dispatch Group

image.png

dispatch_group做爲一個單元監控一組任務。你能夠將一組任務放到一個組裏經過dispatch_group同步他們的行爲。你能夠將這些任務放在組裏之後在同一個隊列或者不一樣的隊列異步執行,能夠在不阻塞當前線程的狀況下監聽這些異步任務執行完畢,也能夠阻塞當前線程等待這些任務完成。

dispatch_group_notify

dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    dispatch_group_async(group, queueGlobal, ^{
        sleep(2);
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueGlobal, ^{
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueGlobal, ^{
        sleep(1);
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_group_notify(group, queueGlobal, ^{
        NSLog(@"notify--%@",[NSThread currentThread]);
    });
    NSLog(@"end--%@",[NSThread currentThread]);
複製代碼

image.png

dispatch_group_notify並無阻塞當前線程,他是在當前組的其餘隊列都執行完成以後再執行,咱們能夠將dispatch_group_notify任務放在主隊列執行,這就實現了回調主線程的功能

dispatch_queue_t queueSerial = dispatch_queue_create("com.serial", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queueMain = dispatch_get_main_queue();
    dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    dispatch_group_async(group, queueGlobal, ^{
        sleep(2);
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueSerial, ^{
        sleep(1);
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueSerial, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_group_notify(group, queueMain, ^{
        NSLog(@"notify--%@",[NSThread currentThread]);
    });
    NSLog(@"end--%@",[NSThread currentThread]);
複製代碼

image.png

dispatch_group_wait

阻塞當前線程,等待組內任務都執行完成纔會繼續執行

dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    dispatch_group_async(group, queueGlobal, ^{
        sleep(2);
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueGlobal, ^{
        sleep(1);
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueGlobal, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 
    NSLog(@"end--%@",[NSThread currentThread]);
複製代碼

image.png

直到前面三個任務都執行完成纔會打印end,固然也能夠將阻塞的超時時間設置小一些,即便前面任務沒有完成,可是時間到了也會繼續執行

dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    dispatch_group_async(group, queueGlobal, ^{
        sleep(3);
        NSLog(@"1--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueGlobal, ^{
        sleep(1);
        NSLog(@"2--%@",[NSThread currentThread]);
    });

    dispatch_group_async(group, queueGlobal, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
    });

    dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2*NSEC_PER_SEC)));
    NSLog(@"end--%@",[NSThread currentThread]);
複製代碼

image.png

dispatch_group_enter、dispatch_group_leave

dispatch_group_async內部也是經過dispatch_group_enterdispatch_group_leave來實現的

dispatch_queue_t queueGlobal = dispatch_get_global_queue(0, 0);

    dispatch_group_enter(group);
    dispatch_async(queueGlobal, ^{
        sleep(3);
        NSLog(@"1--%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });

    dispatch_group_enter(group);
    dispatch_async(queueGlobal, ^{
        sleep(1);
        NSLog(@"2--%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });

    dispatch_group_enter(group);
    dispatch_async(queueGlobal, ^{
        NSLog(@"3--%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });

    dispatch_group_notify(group, queueGlobal, ^{
        NSLog(@"notify--%@",[NSThread currentThread]);
    });

    NSLog(@"end--%@",[NSThread currentThread]);
複製代碼

實現了和dispatch_group_async同樣的效果 image.png

信號量semaphore

在開發中常常須要線程同步,那麼信號量是一個很好的選擇,dispatch_semaphore_signal信號量+1,dispatch_semaphore_wait信號量-1,若是信號量小於0那麼阻塞當前線程,能夠設置阻塞的超時時間

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    dispatch_async(queueGlobal, ^{
        sleep(3);//耗時操做
        NSLog(@"1--%@",[NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);//信號量+1
    });

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//信號量-1
    dispatch_async(queueMain, ^{
        NSLog(@"主線程");
    });
    NSLog(@"end--%@",[NSThread currentThread]);
複製代碼

image.png

多線程訪問同一個數據有可能形成數據不安全,例如

__block int i = 5;
    while (i>0) {
        dispatch_async(queueGlobal, ^{
            i--;
            NSLog(@"%d--%@",i,[NSThread currentThread]);
        });
    }
複製代碼

image.png

因爲多線程同時修改i致使結果和預期出現了很大的出入,甚至NSLog函數的打印都出問題了😂,經過信號量能夠解決這個問題

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    __block int i = 5;
    
    while (i>0) {
        dispatch_async(queueGlobal, ^{
            i--;
            NSLog(@"%d--%@",i,[NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    }
複製代碼

這個時候咱們看到結果按照預期輸出了,由於信號量控制最多隻有一個線程能夠訪問變量i(不必定是同一個線程,但同一時間最多隻能有一個線程訪問)

image.png

dispatch_set_target_queue

dispatch_set_target_queue有兩個做用一是改變隊列優先級,二是讓多個串行隊列之間也能串行地執行任務。

咱們前邊所學的方法都是控制隊列內部的任務的操做順序,可是不一樣隊列之間是沒有依賴關係的,假如咱們把A任務放在自定義串行隊列serialQueueA中,把B任務放在自定義併發隊列conQueueB中,那麼咱們不知道任務A和任務B的前後執行順序,有了dispatch_set_target_queue咱們能夠利用其改變隊列優先級的做用實現優先隊列

改變隊列優先級實現優先隊列

dispatch_queue_create 建立的隊列,不管是串行仍是併發,其優先級都是 DISPATCH_QUEUE_PRIORITY_DEFAULT,使用 dispatch_set_target_queue 能夠改變隊列優先級,注意改變的是優先調度開始順序,而不是結束順序,先被調度的不必定先結束執行

dispatch_queue_t serialQueue1 = dispatch_queue_create("serialQueue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("serialQueue2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue3 = dispatch_queue_create("serialQueue3", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue4 = dispatch_queue_create("serialQueue4", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue5 = dispatch_queue_create("serialQueue5", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue6 = dispatch_queue_create("serialQueue6", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue7 = dispatch_queue_create("serialQueue7", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue8 = dispatch_queue_create("serialQueue8", DISPATCH_QUEUE_SERIAL);

    dispatch_async(serialQueue1, ^{
        NSLog(@"taskA");
    });

    dispatch_async(serialQueue2, ^{
        NSLog(@"taskB");
    });
    
    dispatch_async(serialQueue3, ^{
        NSLog(@"taskC");
    });
    
    dispatch_async(serialQueue4, ^{
        NSLog(@"taskD");
    });
    
    dispatch_async(serialQueue5, ^{
        NSLog(@"taskE");
    });
    
    dispatch_async(serialQueue6, ^{
        NSLog(@"taskF");
    });

    dispatch_async(serialQueue7, ^{
        NSLog(@"taskG");
    });

    dispatch_async(serialQueue8, ^{
        NSLog(@"taskH");
    });
複製代碼

image.png 這個時候咱們不能肯定ABCDEFGH誰先執行,簡單修改一下

dispatch_queue_t serialQueue1 = dispatch_queue_create("serialQueue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("serialQueue2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue3 = dispatch_queue_create("serialQueue3", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue4 = dispatch_queue_create("serialQueue4", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue5 = dispatch_queue_create("serialQueue5", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue6 = dispatch_queue_create("serialQueue6", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue7 = dispatch_queue_create("serialQueue7", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue8 = dispatch_queue_create("serialQueue8", DISPATCH_QUEUE_SERIAL);
    
    dispatch_queue_t globalQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

    dispatch_set_target_queue(serialQueue2, globalQueueLow);

    dispatch_async(serialQueue1, ^{
        NSLog(@"taskA");
    });

    dispatch_async(serialQueue2, ^{
        NSLog(@"taskB");
    });
    
    dispatch_async(serialQueue3, ^{
        NSLog(@"taskC");
    });
    
    dispatch_async(serialQueue4, ^{
        NSLog(@"taskD");
    });
    
    dispatch_async(serialQueue5, ^{
        NSLog(@"taskE");
    });
    
    dispatch_async(serialQueue6, ^{
        NSLog(@"taskF");
    });

    dispatch_async(serialQueue7, ^{
        NSLog(@"taskG");
    });

    dispatch_async(serialQueue8, ^{
        NSLog(@"taskH");
    });
複製代碼

咱們增長了兩行代碼

dispatch_queue_t globalQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    dispatch_set_target_queue(serialQueue2, globalQueueLow);
複製代碼

image.png

網上看了好多例子都是到此爲止,以致於我任務只是由於我給globalQueueLow設置的優先級爲DISPATCH_QUEUE_PRIORITY_BACKGROUND因此serialQueue2的優先級才筆另外幾個串行隊列低,若是我設置優先級爲DISPATCH_QUEUE_PRIORITY_HIGH那麼隊列就會優先調度

dispatch_queue_t serialQueue1 = dispatch_queue_create("serialQueue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("serialQueue2", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue3 = dispatch_queue_create("serialQueue3", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue4 = dispatch_queue_create("serialQueue4", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue5 = dispatch_queue_create("serialQueue5", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue6 = dispatch_queue_create("serialQueue6", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue7 = dispatch_queue_create("serialQueue7", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue8 = dispatch_queue_create("serialQueue8", DISPATCH_QUEUE_SERIAL);
    
    dispatch_queue_t globalQueueLow = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    dispatch_queue_t globalQueueHight = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    
    dispatch_set_target_queue(serialQueue2, globalQueueLow);
    dispatch_set_target_queue(serialQueue3, globalQueueHight);

    dispatch_async(serialQueue1, ^{
        NSLog(@"taskA");
    });

    dispatch_async(serialQueue2, ^{
        NSLog(@"taskB");
    });
    
    dispatch_async(serialQueue3, ^{
        NSLog(@"taskC");
    });
    
    dispatch_async(serialQueue4, ^{
        NSLog(@"taskD");
    });
    
    dispatch_async(serialQueue5, ^{
        NSLog(@"taskE");
    });
    
    dispatch_async(serialQueue6, ^{
        NSLog(@"taskF");
    });

    dispatch_async(serialQueue7, ^{
        NSLog(@"taskG");
    });

    dispatch_async(serialQueue8, ^{
        NSLog(@"taskH");
    });
複製代碼

image.png

多運行幾回咱們發現設置DISPATCH_QUEUE_PRIORITY_HIGH優先級以後串行隊列serialQueue3並無比其餘優先級高,而僅僅比serialQueue2高了而已,咱們前邊也說過,這種優先級高僅僅是開始調度的優先級高,而不是結束調度的優先級,若是咱們給任務A設置一個耗時任務就能夠驗證

dispatch_async(serialQueue1, ^{
        int a=0;
        for (int i=0; i<1000000; i++) {
            a++;
        }
        NSLog(@"%d-taskA",a);
    });
複製代碼

image.png

這時A並無先結束執行,BC也沒有最後結束執行

若是咱們設置

dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_set_target_queue(serialQueue3, mainQueue);
複製代碼

這個時候是否是serialQueue3的優先級會不會很高呢,其實並無

image.png

看了官方文檔也是很難理解dispatch_set_target_queue,我我的理解是若是經過dispatch_set_target_queue設置隊列優先級的這些隊列他們都成了二級公民,依賴於同一個隊列的子隊列之間能夠經過優先級來設置,可是他們的優先級都要比沒有設置過優先級的隊列優先級要低了

image.png

咱們可使用dispatch_set_target_queue+dispatch_suspend/dispatch_resume實現一個優先隊列

dispatch_queue_t serialQueue1 = dispatch_queue_create("serialQueue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("serialQueue2", DISPATCH_QUEUE_SERIAL);
    
    dispatch_set_target_queue(serialQueue1, serialQueue2);

    dispatch_async(serialQueue1, ^{
        NSLog(@"taskA");
    });

    dispatch_suspend(serialQueue1);
    dispatch_async(serialQueue2, ^{
        NSLog(@"taskB");
        dispatch_resume(serialQueue1);
    });

    dispatch_async(serialQueue1, ^{
        NSLog(@"taskC");
    });
複製代碼

參考文章

iOS 多線程:『GCD』詳盡總結

Dispatch

源碼libdispatch

iOS多線程之dispatch_once剖析

dispatch_set_target_queue

《iOS 與 OS X 多線程和內存管理》讀書筆記之 GCD(二)

相關文章
相關標籤/搜索