iOS GCD (一)

概述

串行隊(duì)列和并行隊(duì)列

串行隊(duì)列 - Serial Dispatch Queue
  • 一個(gè)串行隊(duì)列只存在一個(gè)線程,同時(shí)執(zhí)行的處理數(shù)只有一個(gè),并且是按順序執(zhí)行;
  • 在多個(gè)線程更新相同資源導(dǎo)致數(shù)據(jù)競(jìng)爭(zhēng)時(shí)使用Serial Dispatch Queue;
  • 串行隊(duì)列創(chuàng)建
// 第一個(gè)參數(shù)為隊(duì)列的名稱;第二個(gè)參數(shù)填NULL(或者DISPATCH_QUEUE_SERIAL)即為串行隊(duì)列
dispatch_queue_t serialDispatchQueue = dispatch_queue_create("serialDispatchQueue_0", NULL);
并行隊(duì)列 - Concurrent Dispatch Queue
  • 一個(gè)并行隊(duì)列有多個(gè)線程,同時(shí)執(zhí)行多個(gè)處理,處理之間無(wú)順序;
  • 當(dāng)想并行執(zhí)行不發(fā)生數(shù)據(jù)競(jìng)爭(zhēng)等問(wèn)題的處理時(shí),使用Concurrent Dispatch Queue
  • 并行隊(duì)列的創(chuàng)建
// 第二個(gè)參數(shù)填DISPATCH_QUEUE_CONCURRENT即為并行隊(duì)列
dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("concurrentDispatchQueue_0", DISPATCH_QUEUE_CONCURRENT);
并行執(zhí)行的處理的數(shù)量問(wèn)題

并行執(zhí)行的處理可能出現(xiàn)的情況有多種:

  • 創(chuàng)建多個(gè) Serial Dispatch Queue;
  • 一個(gè)Concurrent Dispatch Queue中有多個(gè)處理,或者多個(gè)Concurrent Dispatch Queue;
  • 同時(shí)存在Serial Dispatch Queue和Concurrent Dispatch Queue。

在這些情況中,只有Concurrent Dispatch Queue中的處理的數(shù)量是取決于當(dāng)前系統(tǒng)的狀態(tài)的,即iOS和OS X基于 Dispatch Queue 中的處理數(shù)、CPU核數(shù)以及CPU負(fù)荷等當(dāng)前系統(tǒng)的狀態(tài)。iOS和OS X的核心 --- XNU內(nèi)核決定應(yīng)當(dāng)使用的線程數(shù),并只生成所需的線程執(zhí)行處理,另外,當(dāng)執(zhí)行的處理數(shù)減少時(shí),XNU內(nèi)核會(huì)結(jié)束不需要的線程。

對(duì)于其他的情況,處理的數(shù)量不受限制,大量生成就會(huì)占用資源,消耗大量?jī)?nèi)存,尤其是生成大量的 Serial Dispatch Queue。

內(nèi)存管理

在iOS6.0(包括6.0)和OS X10.8(包括10.8)之后的版本中,開(kāi)啟ARC的情況下不需要通過(guò)dispatch_release()手動(dòng)管理釋放GCD對(duì)象;
而在此版本之前是需要手動(dòng)管理的,略去不表。

Dispatch Queue的使用

通過(guò)異步添加進(jìn)隊(duì)列的方法dispatch_async()往隊(duì)列里面添加要執(zhí)行的任務(wù),如下:

dispatch_queue_t serialDispatchQueue = dispatch_queue_create("serialDispatchQueue_0", NULL);
dispatch_async(serialDispatchQueue, ^{
    NSLog(@"串行隊(duì)列中的任務(wù)");
});
dispatch_queue_t concurrentDispatchQueue = dispatch_queue_create("concurrentDispatchQueue_0", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentDispatchQueue, ^{
    NSLog(@"并行隊(duì)列中的任務(wù)");
});
同步添加任務(wù)和異步添加任務(wù)

異步添加任務(wù)的方法:dispatch_async()

不會(huì)等待添加進(jìn)隊(duì)列的任務(wù)執(zhí)行,就會(huì)執(zhí)行后面的代碼,即異步執(zhí)行。如下代碼:

    dispatch_queue_t serialDispatchQueue = dispatch_queue_create("serialDispatchQueue_0", NULL);
    NSLog(@"1");
    dispatch_async(serialDispatchQueue, ^{
        NSLog(@"串行隊(duì)列中的任務(wù)");
    });
    NSLog(@"2");

結(jié)果如下:

2018-09-30 16:08:47.066070+0800 GCDTest[15737:1710164] 1
2018-09-30 16:08:47.066279+0800 GCDTest[15737:1710164] 2
2018-09-30 16:08:47.066334+0800 GCDTest[15737:1710258] 串行隊(duì)列中的任務(wù)

同步添加任務(wù)的方法:dispatch_sync()

會(huì)等待添加進(jìn)隊(duì)列的任務(wù)執(zhí)行完成,然后再執(zhí)行后面的代碼,會(huì)阻塞線程,如下代碼:

dispatch_queue_t serialDispatchQueue = dispatch_queue_create("serialDispatchQueue_0", NULL);
NSLog(@"1");
dispatch_sync(serialDispatchQueue, ^{
    NSLog(@"串行隊(duì)列中的任務(wù)");
});
NSLog(@"2");

結(jié)果如下:

2018-09-30 16:02:02.730673+0800 GCDTest[15673:1700905] 1
2018-09-30 16:02:02.730834+0800 GCDTest[15673:1700905] 串行隊(duì)列中的任務(wù)
2018-09-30 16:02:02.730920+0800 GCDTest[15673:1700905] 2

使用這個(gè)函數(shù)添加任務(wù),容易導(dǎo)致死鎖,尤其是在串行隊(duì)列,幾乎不能使用dispatch_sync函數(shù),看下面代碼

    dispatch_queue_t mainqueue = dispatch_get_main_queue();
    dispatch_sync(mainqueue, ^{
        NSLog(@"hellow");
    });

按照代碼的意思,主隊(duì)列執(zhí)行dispatch_sync函數(shù),如果執(zhí)行完追加到主隊(duì)列的blcok就會(huì)接著往下走,但是dispatch_sync卻要等待主隊(duì)列執(zhí)行完dispatch_sync函數(shù)的代碼,才會(huì)執(zhí)行追加到主隊(duì)列的block,這樣就出現(xiàn)的了相互等待,導(dǎo)致死鎖。下面的代碼一樣會(huì)導(dǎo)致死鎖

    dispatch_queue_t mainqueue = dispatch_get_main_queue();
    dispatch_async(mainqueue, ^{
        dispatch_sync(mainqueue, ^{
            NSLog(@"hellow");
        });        
    });

串行隊(duì)列總是按順序執(zhí)行,雖然是異步追加,主隊(duì)列也是要等待block執(zhí)行完成才會(huì)往下執(zhí)行。

Main Dispatch Queue和Global Dispatch Queue

Main Dispatch Queue是在主線程執(zhí)行的隊(duì)列,因?yàn)橹骶€程只有1個(gè),所以為Serial Dispatch Queue。添加到Main Dispatch Queue中的任務(wù),在主線程的RunLoop中執(zhí)行,所以更新界面的操作一般追加到Main Dispatch Queue中

    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_async(mainQueue, ^{
        NSLog(@"主線程執(zhí)行任務(wù)");
    });

Global Dispatch Queue是所有應(yīng)用程序都能夠使用的Concurrent Dispatch Queue,有四個(gè)優(yōu)先級(jí)

  • Height Priority --- DISPATCH_QUEUE_PRIORITY_HIGH
  • Default Priority --- DISPATCH_QUEUE_PRIORITY_DEFAULT
  • Low Priority --- DISPATCH_QUEUE_PRIORITY_LOW
  • Background Priority --- DISPATCH_QUEUE_PRIORITY_BACKGROUND
    // 第二個(gè)參數(shù)是備用的,填0就可以
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(globalQueue, ^{
        NSLog(@"全局隊(duì)列執(zhí)行任務(wù)");
    });

dispatch_queue_create創(chuàng)建的隊(duì)列,無(wú)論是Serial Dispatch Queue還是Concurrent Dispatch Queue默認(rèn)優(yōu)先級(jí)均為 DISPATCH_QUEUE_PRIORITY_DEFAULT

dispatch_set_target_queue

dispatch_set _target_queue(參數(shù)一,參數(shù)二 ),參數(shù)一是需要變更的隊(duì)列,參數(shù)二是目標(biāo)隊(duì)列,最后兩個(gè)隊(duì)列的優(yōu)先級(jí)別相同,均為目標(biāo)隊(duì)列的優(yōu)先級(jí)。

dispatch_set_target_queue有兩個(gè)作用,即變更隊(duì)列的優(yōu)先級(jí)和使目標(biāo)隊(duì)列變?yōu)閳?zhí)行階層

  • 變更隊(duì)列優(yōu)先級(jí)
    dispatch_queue_t serialQueue_0 = dispatch_queue_create("serialQueue_0", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t targetQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
    
    dispatch_async(serialQueue_0, ^{
        NSLog(@"queue_0");
    });
    dispatch_set_target_queue(serialQueue_0, targetQueue);

這樣隊(duì)列serialQueue_0的優(yōu)先級(jí)與targetQueue隊(duì)列的優(yōu)先級(jí)相同,但是我覺(jué)得并沒(méi)有什么用,看下面的代碼:

    dispatch_queue_t serialQueue_0 = dispatch_queue_create("serialQueue_0", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue_1 = dispatch_queue_create("serialQueue_1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t targetQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

    dispatch_async(serialQueue_0, ^{
        NSLog(@"queue_0");
    });
    dispatch_async(serialQueue_1, ^{
        NSLog(@"queue_1");
    });
    dispatch_set_target_queue(serialQueue_0, targetQueue);
    
    dispatch_async(serialQueue_0, ^{
        NSLog(@"queue_0");
    });
    dispatch_async(serialQueue_1, ^{
        NSLog(@"queue_1");
    });

按照上面的代碼分析,應(yīng)該隊(duì)列serialQueue_0在修改優(yōu)先級(jí)之后,其優(yōu)先級(jí)低于serialQueue_1,可是運(yùn)行結(jié)果不是一定的,如下

2018-10-08 10:40:35.329235+0800 GCDTest[3623:1303883] queue_0
2018-10-08 10:40:35.329236+0800 GCDTest[3623:1303885] queue_1
2018-10-08 10:40:35.329406+0800 GCDTest[3623:1303885] queue_1
2018-10-08 10:40:35.329422+0800 GCDTest[3623:1303883] queue_0
2018-10-08 10:37:27.117724+0800 GCDTest[3595:1298718] queue_1
2018-10-08 10:37:27.117724+0800 GCDTest[3595:1298715] queue_0
2018-10-08 10:37:27.117930+0800 GCDTest[3595:1298718] queue_1
2018-10-08 10:37:27.117934+0800 GCDTest[3595:1298715] queue_0

由于沒(méi)有一個(gè)像樣的方法(dipatch_queue_attr_make_with_qos_class函數(shù)可以修改,不過(guò)不太一樣)去指定queue_1的優(yōu)先級(jí),所以個(gè)人覺(jué)得,改變優(yōu)先級(jí)的功能少用。

  • 使目標(biāo)隊(duì)列變?yōu)閳?zhí)行階層

如果將多個(gè)Serial Dispatch Queue使用dispatch_set_target_queue指定到了同一目標(biāo)(Serial Dispatch Queue),那么這些串行queue在目標(biāo)queue上就是同步執(zhí)行的,不再是并行執(zhí)行

    dispatch_queue_t serialQueue_0 = dispatch_queue_create("serialQueue_0", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue_1 = dispatch_queue_create("serialQueue_1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue_2 = dispatch_queue_create("serialQueue_3", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t targetQueue = dispatch_queue_create("serialQueue_target", DISPATCH_QUEUE_SERIAL);

    dispatch_set_target_queue(serialQueue_0, targetQueue);
    dispatch_set_target_queue(serialQueue_1, targetQueue);
    dispatch_set_target_queue(serialQueue_2, targetQueue);
    
    dispatch_async(serialQueue_0, ^{
        NSLog(@"queue_0");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"queue_0 sleep");
    });
    dispatch_async(serialQueue_1, ^{
        NSLog(@"queue_1");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"queue_1 sleep");
    });
    dispatch_async(serialQueue_2, ^{
        NSLog(@"queue_2");
        [NSThread sleepForTimeInterval:2.f];
        NSLog(@"queue_2 sleep");
    });

執(zhí)行結(jié)果如下:

2018-10-08 10:58:11.531291+0800 GCDTest[3844:1332951] queue_0
2018-10-08 10:58:13.531850+0800 GCDTest[3844:1332951] queue_0 sleep
2018-10-08 10:58:13.532047+0800 GCDTest[3844:1332951] queue_1
2018-10-08 10:58:15.533480+0800 GCDTest[3844:1332951] queue_1 sleep
2018-10-08 10:58:15.533886+0800 GCDTest[3844:1332951] queue_2
2018-10-08 10:58:17.538822+0800 GCDTest[3844:1332951] queue_2 sleep

修改下代碼,注釋掉如下代碼

//    dispatch_set_target_queue(serialQueue_0, targetQueue);
//    dispatch_set_target_queue(serialQueue_1, targetQueue);
//    dispatch_set_target_queue(serialQueue_2, targetQueue);

運(yùn)行結(jié)果如下:

2018-10-08 11:03:23.287384+0800 GCDTest[3899:1341109] queue_1
2018-10-08 11:03:23.287384+0800 GCDTest[3899:1341112] queue_2
2018-10-08 11:03:23.287420+0800 GCDTest[3899:1341110] queue_0
2018-10-08 11:03:25.292805+0800 GCDTest[3899:1341110] queue_0 sleep
2018-10-08 11:03:25.292798+0800 GCDTest[3899:1341112] queue_2 sleep
2018-10-08 11:03:25.292798+0800 GCDTest[3899:1341109] queue_1 sleep

這樣就明白,dispatch_set_target_queue函數(shù)之使目標(biāo)隊(duì)列變成執(zhí)行階層的含義了

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

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

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