「面向打野編程」iOS多線程:CGD

「面向打野編程」iOS多線程:CGDweb

前言

參考網絡其餘文章而寫,渣水平,拋磚引玉。編程

雖然Concurrent意思爲併發,但因爲隊列的實際效果,如下稱爲並行隊列。網絡

當前iPhone的CPU核心數遠小於GCD的線程池,故不討論GCD的線程池,沒有意義。多線程

GCD = 主隊列 + 並行隊列 * n併發

異步串行隊列 = 並行隊列 * 1app

1. 同異步隊列

  • 主隊列dispatch_get_main_queue()異步

  • 全局並行隊列dispatch_get_global_queue(0, 0)async

  • 串行隊列dispatch_queue_t sQue = dispatch_queue_create("q1", DISPATCH_QUEUE_SERIAL);ide

  • 並行隊列dispatch_queue_t cQue = dispatch_queue_create("q2", DISPATCH_QUEUE_CONCURRENT);測試

GCD 串行隊列 並行隊列
同步執行 搶佔執行 搶佔執行
異步執行 等待執行 並行執行
  • 同步 + 隊列 = 主隊列
  • (異步 + 並行) * n = 串行隊列 * n
  • 串行(同步 + 自身) = 死鎖(沒法搶佔)

同步執行:搶佔當前主線程,不能在主線程中使用,會形成死鎖。

異步執行:將任務加入隊列末尾,或將任務加入新線程。


主隊列並行隊列互不影響,即便並行隊列被長耗時任務佔滿,主隊列依然暢通。

通過測試,全局並行隊列與自定義並行隊列並無太多明顯的區別。當全局並行隊列佔滿核心後,自定義並行隊列依然會陷入等待,沒法併發。

dispatch_queue_t cQue = dispatch_queue_create("cq", DISPATCH_QUEUE_CONCURRENT);
for(int i = 0; i < 20; ++i) {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for(int j = 1; j < INT_MAX; ++j);
        for(int j = 1; j < INT_MAX; ++j);
        printf("_");
    });
}
for(int i = 0; i < 20; ++i) {
    dispatch_async(cQue, ^{
        printf("2");
    });
}
________________22222222222222222222____

CPU的核心數量是有限的,當異步線程的數量主夠多的時候,會退化爲串行隊列,並不會變爲併發執行。對於多個長時間運行的並行線程,可能會由於佔滿處理器而致使其餘線程任務的堆積,可使用[NSThread sleepForTimeInterval:(NSTimeInterval)]手動分割控制阻塞實現僞併發操做來改善長時間等待。

[NSThread sleepForTimeInterval:(NSTimeInterval)]

dispatch_queue_t cQue = dispatch_queue_create("cq", DISPATCH_QUEUE_CONCURRENT);
for(int i = 0; i < 20; ++i) {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for(int j = 1; j < INT_MAX>>1; ++j);
        [NSThread sleepForTimeInterval:2];
        for(int j = 1; j < INT_MAX>>1; ++j);
        [NSThread sleepForTimeInterval:1];
        for(int j = 1; j < INT_MAX>>1; ++j);
        [NSThread sleepForTimeInterval:0.5];
        for(int j = 1; j < INT_MAX>>1; ++j);
        printf("_");
    });
}
for(int i = 0; i < 20; ++i) {
    dispatch_async(cQue, ^{
        printf("2");
    });
}
_____22222222222222222222_______________

手動分割

dispatch_queue_t cQue = dispatch_queue_create("cq", DISPATCH_QUEUE_CONCURRENT);
for(int i = 0; i < 20; ++i) {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        for(int j = 1; j < INT_MAX>>1; ++j);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            for(int j = 1; j < INT_MAX>>1; ++j);
            dispatch_async(dispatch_get_global_queue(0, 0), ^{
                for(int j = 1; j < INT_MAX>>1; ++j);
                dispatch_async(dispatch_get_global_queue(0, 0), ^{
                    for(int j = 1; j < INT_MAX>>1; ++j);
                    printf("_");
                });
            });
        });
    });
}
for(int i = 0; i < 20; ++i) {
    dispatch_async(cQue, ^{
        printf("2");
    });
}
22222222222222222222____________________

2. 柵欄

  • 同步柵欄dispatch_barrier_async

  • 異步柵欄dispatch_barrier_async

柵欄對全局並行隊列無效,只能操做自定義並行隊列。想要運行此處的代碼就須要完成先前的操做,是否同步的區別在於可否阻塞當前線程。千萬不要對把同步柵欄運行在同個隊列,會形成死鎖。


同步柵欄

dispatch_queue_t cQue = dispatch_queue_create("q", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(cQue, ^{
    for(int j = 1; j < INT_MAX; ++j);
    puts("1");
});
dispatch_async(cQue, ^{
    for(int j = 1; j < INT_MAX; ++j);
    puts("2");
});
dispatch_async(cQue, ^{
    for(int j = 1; j < INT_MAX; ++j);
    puts("3");
});
dispatch_barrier_sync(cQue, ^{
    NSLog(@"4");
});
puts("-----5-------");
3
2
1
4
-----5-------

異步柵欄

dispatch_queue_t cQue = dispatch_queue_create("q", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(cQue, ^{
    for(int j = 1; j < INT_MAX; ++j);
    puts("1");
});
dispatch_async(cQue, ^{
    for(int j = 1; j < INT_MAX; ++j);
    puts("2");
});
dispatch_async(cQue, ^{
    for(int j = 1; j < INT_MAX; ++j);
    puts("3");
});
dispatch_barrier_async(cQue, ^{
    NSLog(@"4");
});
puts("-----5-------");
-----5-------
1
2
3
4

死鎖

dispatch_queue_t cQue2 = dispatch_queue_create("q22", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(cQue2, ^{
    dispatch_async(cQue2, ^{
        puts("1");
    });
    dispatch_barrier_sync(cQue2, ^{
        puts("----------");
    });
    dispatch_async(cQue2, ^{
        puts("2");
    });
});
1

3. 延遲執行

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    //延遲0.5s執行
});

4. 一次性代碼

只執行一次,可是須要注意不要出現相互調用,會出現死鎖

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    //code to be executed once
});

5. 快速遍歷

不用手寫循環,或同時執行多個類似的代碼塊,會阻塞當前線程。

dispatch_apply(6, dispatch_get_global_queue(0, 0), ^(size_t i) {
    NSLog(@"%zu", i);
});
2017-12-15 11:34:56.748284+0800 GCD-OC[2266:105786] 1
2017-12-15 11:34:56.748260+0800 GCD-OC[2266:105750] 0
2017-12-15 11:34:56.748294+0800 GCD-OC[2266:105785] 2
2017-12-15 11:34:56.748302+0800 GCD-OC[2266:105788] 3
2017-12-15 11:34:56.749694+0800 GCD-OC[2266:105750] 4
2017-12-15 11:34:56.749703+0800 GCD-OC[2266:105786] 5

6. 信號量

控制最大並行數,或用於同步,然而容易被其餘方法取代。

  • 建立信號量dispatch_semaphore_create(long value)

至關於並行數,運行同時執行的線程數。

  • 獲取信號量dispatch_semaphore_wait(dispatch_semaphore_t _Nonnull dsema, dispatch_time_t timeout)

若是信號量,返回0表示獲取成功,不然阻塞主線程開始等待,超過等待時間後繼續執行代碼。

  • 增長信號量dispatch_semaphore_signal(dispatch_semaphore_t _Nonnull dsema)

信號量+1。

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
long a = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
puts(a ? "1" : "0");
a = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
puts(a ? "1" : "0");
dispatch_semaphore_signal(semaphore);
for(int i = 0; i < 6; ++i) {
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        long a = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        sleep(3);
        NSLog(a ? @"YES" : @"NO");
        dispatch_semaphore_signal(semaphore);
    });
}
puts("----------");
0
1
----------
2017-12-15 15:55:11.589061+0800 GCD-OC[4209:250818] NO
2017-12-15 15:55:14.593238+0800 GCD-OC[4209:250816] NO
2017-12-15 15:55:17.594262+0800 GCD-OC[4209:250817] NO
2017-12-15 15:55:20.597262+0800 GCD-OC[4209:250815] NO
2017-12-15 15:55:23.599650+0800 GCD-OC[4209:250850] NO
2017-12-15 15:55:26.603327+0800 GCD-OC[4209:250851] NO

7. dispatch_group

先完成前面的任務,再執行最後一個任務。至關於異步柵欄,但能夠將任務給予任意隊列。

建立groupdispatch_group_t group = dispatch_group_create();

添加任務

dispatch_group_async(dispatch_group_t  _Nonnull group, dispatch_queue_t  _Nonnull queue, ^{
    code
});

最終任務

dispatch_group_notify(dispatch_group_t  _Nonnull group, dispatch_queue_t  _Nonnull queue, ^{
    code
});

dispatch_queue_t cQue1 = dispatch_queue_create("q11", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t cQue2 = dispatch_queue_create("q22", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, cQue1, ^(){
    sleep(2);
    puts("1");
});
dispatch_group_async(group, cQue2, ^(){
    sleep(1);
    puts("2");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^(){
    puts("3");
});
puts("-------");
-------
2
1
3
相關文章
相關標籤/搜索