小笨狼漫談多線程:GCD(一)

多線程是程序開發(fā)中非常基礎(chǔ)的一個概念,大家在開發(fā)過程中應(yīng)該或多或少用過相關(guān)的東西艇炎。同時這恰恰又是一個比較棘手的概念,一切跟多線程掛鉤的東西都會變得復(fù)雜报亩。如果使用過程中對多線程不夠熟悉,很可能會埋下一些難以預(yù)料的坑井氢。

iOS中的多線程技術(shù)主要有NSThread, GCD和NSOperation弦追。他們的封裝層次依次遞增,其中

  • NSThread封裝性最差花竞,最偏向于底層劲件,主要基于thread使用
  • GCD是基于C的API掸哑,直接使用比較方便,主要基于task使用
  • NSOperation是基于GCD封裝的NSObject對象零远,對于復(fù)雜的多線程項目使用比較方便苗分,主要基于隊列使用

上篇文章介紹了NSThread的用法,NSThread已經(jīng)屬于古董級別的東西了牵辣,欣賞一下可以摔癣,真正使用就不要麻煩他了。GCD是多線程中的新貴纬向,比起NSThread更加強大择浊,也更容易使用。由于GCD的東西比較多逾条,我會分好幾篇文章介紹琢岩,這篇文章主要介紹GCD中的queue相關(guān)知識。

dispatch_queue_t

使用GCD之后师脂,你可以不用再浪費精力去關(guān)注線程担孔,GCD會幫你管理好一切。你只需要想清楚任務(wù)的執(zhí)行方法(同步還是異步)和隊列的運行方式(串行還是并行)即可吃警。

任務(wù)是一個比較抽象的概念糕篇,表示一段用來執(zhí)行的代碼,他對應(yīng)到代碼里就是一個block或者一個函數(shù)汤徽。

隊列分為串行隊列和并行隊列:

  • 串行隊列一次只能執(zhí)行一個任務(wù)。只有一個任務(wù)執(zhí)行完成之后灸撰,下一個任務(wù)才能執(zhí)行谒府,主線程就是一個串行的隊列。


  • 并行隊列可以同時執(zhí)行多個任務(wù)浮毯,系統(tǒng)會維護一個線程池來保證并行隊列的執(zhí)行完疫。線程池會根據(jù)當前任務(wù)量自行安排線程的數(shù)量,以確保任務(wù)盡快執(zhí)行债蓝。


隊列對應(yīng)到代碼里是一個dispatch_queue_t對象:

dispatch_queue_t queue;

對象就有內(nèi)存壳鹤。跟普通OC對象類似,我們可以用dispatch_retain()dispatch_release()對其進行內(nèi)存管理饰迹,當一個任務(wù)加入到一個queue中的時候芳誓,任務(wù)會retain這個queue,直到任務(wù)執(zhí)行完成才會release啊鸭。

值得高興的是锹淌,iOS6之后,dispatch對象已經(jīng)支持ARC赠制,所以在ARC工程之下赂摆,我們可以不用擔心他的內(nèi)存,想怎么玩就怎么玩。

要申明一個dispatch的屬性烟号。一般情況下我們只需要用strong即可绊谭。

@property (nonatomic, strong) dispatch_queue_t queue;

如果你是寫一個framework,framework的使用者的SDK有可能還是古董級的iOS6之前汪拥。那么你需要根據(jù)OS_OBJECT_USE_OBJC做一個判斷是使用strong還是assign达传。(一般github上的優(yōu)秀第三方庫都會這么做)

#if OS_OBJECT_USE_OBJC
@property (nonatomic, strong) dispatch_queue_t queue;
#else
@property (nonatomic, assign) dispatch_queue_t queue;
#endif

async

GCD中有2個異步的API

void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work);

他們都是將一個任務(wù)提交到queue中,提交之后立即返回喷楣,不等待任務(wù)的的執(zhí)行趟大。提交之后,系統(tǒng)會對queue做retain操作铣焊,任務(wù)執(zhí)行完成之后逊朽,queue再被release曲伊。兩個函數(shù)實際的功能是一樣的,唯一的區(qū)別在于dispatch_async接受block作為參數(shù)赚哗,dispatch_async_f接受函數(shù)。

使用dispatch_async的時候block會被copy,在block執(zhí)行完成之后block再release,由于是系統(tǒng)持有block期丰,所以不用擔心循環(huán)引用的問題,block里面的self不需要weak

dispatch_async_f中,context會作為第一個參數(shù)傳給work函數(shù)荣病。如果work不需要參數(shù)颊亮,context可以傳入NULL雹有。work參數(shù)不能傳入NULL脯厨,否則可能發(fā)生無法預(yù)料的事兒

異步是一個比較抽象的概念稼跳,簡單的說就是將任務(wù)加入到隊列中之后,立即返回摇零,不需要等待任務(wù)的執(zhí)行噪服。語言的描述比較抽象邪铲,我們用代碼加深一下對概念的理解

NSLog(@"this is main queue, i want to throw a task to global queue");
dispatch_queue_t globalQueue = dispatch_queue_create("com.liancheng.global_queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(globalQueue, ^{
    // task
});
NSLog(@"this is main queue, throw task completed");

上面這段代碼,會以這樣的方式運行,紅色表示正在執(zhí)行的模塊哎垦,灰色表示未執(zhí)行或者已經(jīng)執(zhí)行完成的模塊鸳碧。

async.gif
  1. 先在main queue中執(zhí)行第一個nslog
  2. dispatch_async會將block提交到globalQueue中日裙,提交成功之后立即返回
  3. main queue執(zhí)行第二個nslog
  4. 等global queue中block前面的任務(wù)執(zhí)行完成之后,block被執(zhí)行。

sync

與異步相似亿卤,GCD中同步的API也是2個

void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work);

2個API作用相同:將任務(wù)提交到queue中,任務(wù)加入queue之后不會立即返回,等待任務(wù)執(zhí)行完成之后再返回延蟹。同sync類似缰儿,dispatch_syncdispatch_sync_f唯一的區(qū)別在于dispatch_sync接收block作為參數(shù)犁罩,block被系統(tǒng)持有碑韵,不需要對self使用weak。dispatch_sync_f接受函數(shù)work作為參數(shù)豁遭,context作為傳給work函數(shù)的第一個參數(shù)。同樣捂蕴,work參數(shù)也不能傳入NULL启绰,否則會發(fā)生無法預(yù)料的事兒

同步表示任務(wù)加入到隊列中之后不會立即返回委可,等待任務(wù)完成再返回。語言的描述比較抽象卡者,我們再次用代碼加深一下對概念的理解

NSLog(@"this is main queue, i want to throw a task to global queue");
dispatch_queue_t globalQueue = dispatch_queue_create("com.liancheng.global_queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(globalQueue, ^{
    // task
});
NSLog(@"this is main queue, throw task completed");

我們來看看代碼的運行方式:

sync.gif
  1. 先在main queue中執(zhí)行第一個nslog
  2. dispatch_sync會將block提交到global queue中,等待block的執(zhí)行
  3. global queue中block前面的任務(wù)執(zhí)行完成之后恒傻,block執(zhí)行
  4. block執(zhí)行完成之后盈厘,dispatch_sync返回
  5. dispatch_sync之后的代碼執(zhí)行

由于dispatch_sync需要等待block被執(zhí)行,這就非常容易發(fā)生死鎖契吉。如果一個串行隊列诡渴,使用dispatch_sync提交block到自己隊列中租悄,就會發(fā)生死鎖

dispatch_queue_t queue = dispatch_queue_create("com.liancheng.serial_queue", DISPATCH_QUEUE_SERIAL);

dispatch_async(queue, ^{
    // 到達串行隊列
    
    dispatch_sync(queue, ^{     //發(fā)生死鎖
        
    });
});

dispatch_sync的代碼執(zhí)行如圖所示

deadLock.png

dispatch_sync需要等待block執(zhí)行完成泣棋,同時由于隊列串行,block的執(zhí)行需要等待前面的任務(wù)把敢,也就是dispatch_sync執(zhí)行完成修赞。兩者互相等待柏副,永遠也不會執(zhí)行完成割择,死鎖就這樣發(fā)生了

從這里看發(fā)生死鎖需要2個條件:

  1. 代碼運行的當前隊列是串行隊列
  2. 使用sync將任務(wù)加入到自己隊列中

如果queue是并行隊列,或者將任務(wù)加入到其他隊列中玛歌,這是不會發(fā)生死鎖的。

獲取隊列

獲取主線程隊列

主線程是我們最常用的線程译荞,GCD提供了非常簡單的獲取主線程隊列的方法吞歼。

dispatch_queue_t dispatch_get_main_queue(void)

方法不需要傳入?yún)?shù),直接返回主線程隊列糯俗。
假設(shè)我們要在主線程更新UI:

dispatch_async(dispatch_get_main_queue(), ^{
    [self updateUI];
});

執(zhí)行加入到主線程隊列的block杖玲,App會調(diào)用dispatch_main(), NSApplicationMain()摆马,或者在主線程使用CFRunLoop

獲取全局隊列

除了主線程隊列蕉毯,GCD提供了幾個全局隊列,可以直接獲取使用

dispatch_queue_t dispatch_get_global_queue(long identifier, unsigned long flags);

dispatch_get_global_queue方法獲取的全局隊列都是并行隊列褐着,并且隊列不能被修改,也就是說對全局隊列調(diào)用dispatch_suspend(), dispatch_resume(), dispatch_set_context()等方法無效

  • identifier: 用以標識隊列優(yōu)先級馅扣,推薦用qos_class枚舉作為參數(shù),也可以使用dispatch_queue_priority_t
  • flags: 預(yù)留字段蓄喇,傳入任何非0的值都可能導(dǎo)致返回NULL

可以看到dispatch_get_global_queue根據(jù)identifier參數(shù)返回相應(yīng)的全局隊列。identifier推薦使用qos_class枚舉

__QOS_ENUM(qos_class, unsigned int,
    QOS_CLASS_USER_INTERACTIVE
            __QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x21,
    QOS_CLASS_USER_INITIATED
            __QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x19,
    QOS_CLASS_DEFAULT
            __QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x15,
    QOS_CLASS_UTILITY
            __QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x11,
    QOS_CLASS_BACKGROUND
            __QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x09,
    QOS_CLASS_UNSPECIFIED
            __QOS_CLASS_AVAILABLE_STARTING(__MAC_10_10, __IPHONE_8_0) = 0x00,
);

這個枚舉與NSThread中的NSQualityOfService類似

  • QOS_CLASS_USER_INTERACTIVE: 最高優(yōu)先級,交互級別愉烙。使用這個優(yōu)先級會占用幾乎所有的系統(tǒng)CUP和I/O帶寬步责,僅限用于交互的UI操作伦忠,比如處理點擊事件,繪制圖像到屏幕上脓匿,動畫等
  • QOS_CLASS_USER_INITIATED: 次高優(yōu)先級陪毡,用于執(zhí)行類似初始化等需要立即返回的事件
  • QOS_CLASS_DEFAULT: 默認優(yōu)先級,當沒有設(shè)置優(yōu)先級的時候,線程默認優(yōu)先級丐谋。一般情況下用的都是這個優(yōu)先級
  • QOS_CLASS_UTILITY: 普通優(yōu)先級,主要用于不需要立即返回的任務(wù)
  • QOS_CLASS_BACKGROUND: 后臺優(yōu)先級萧落,用于用戶幾乎不感知的任務(wù)陨倡。
  • QOS_CLASS_UNSPECIFIED: 未知優(yōu)先級兴革,表示服務(wù)質(zhì)量信息缺失

identifier除了使用qos_class枚舉蜜唾,也可以用dispatch_queue_priority_t作為參數(shù)袁余。

#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

typedef long dispatch_queue_priority_t;

dispatch_queue_priority_t對應(yīng)到qos_class枚舉有:

  - DISPATCH_QUEUE_PRIORITY_HIGH:         QOS_CLASS_USER_INITIATED
  - DISPATCH_QUEUE_PRIORITY_DEFAULT:      QOS_CLASS_DEFAULT
  - DISPATCH_QUEUE_PRIORITY_LOW:          QOS_CLASS_UTILITY
  - DISPATCH_QUEUE_PRIORITY_BACKGROUND:   QOS_CLASS_BACKGROUND

很多時候我們喜歡將0或者NULL傳入作為參數(shù)

dispatch_get_global_queue(NULL, NULL)

由于NULL等于0,也就是DISPATCH_QUEUE_PRIORITY_DEFAULT棚饵,所以返回的是默認優(yōu)先級

創(chuàng)建隊列

當無法獲取到理想的隊列時噪漾,我們可以自己創(chuàng)建隊列且蓬。

dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

如果未使用ARC恶阴,dispatch_queue_create創(chuàng)建的queue在使用結(jié)束之后需要調(diào)用dispatch_release存淫。

  • label: 隊列的名稱桅咆,調(diào)試的時候可以區(qū)分其他的隊列
  • attr: 隊列的屬性荚虚,dispatch_queue_attr_t類型。用以標識隊列串行渴析,并行咆疗,以及優(yōu)先級等信息

attr參數(shù)有三種傳值方式:

// 串行
#define DISPATCH_QUEUE_SERIAL NULL

// 并行
#define DISPATCH_QUEUE_CONCURRENT \
        DISPATCH_GLOBAL_OBJECT(dispatch_queue_attr_t, \
        _dispatch_queue_attr_concurrent)

// 自定義屬性值
dispatch_queue_attr_t dispatch_queue_attr_make_with_qos_class(dispatch_queue_attr_t attr, dispatch_qos_class_t qos_class, int relative_priority);

DISPATCH_QUEUE_SERIAL或者NULL,表示創(chuàng)建串行隊列迅皇,優(yōu)先級為目標隊列優(yōu)先級。DISPATCH_QUEUE_CONCURRENT表示創(chuàng)建并行隊列挺据,優(yōu)先級也為目標隊列優(yōu)先級暇检。

dispatch_queue_attr_make_with_qos_class函數(shù)可以創(chuàng)建帶有優(yōu)先級的dispatch_queue_attr_t對象构蹬。通過這個對象可以自定義queue的優(yōu)先級。

  • attr: 傳入DISPATCH_QUEUE_SERIAL藻烤、NULL或者DISPATCH_QUEUE_CONCURRENT,表示串行或者并行
  • qos_class: 傳入qos_class枚舉,表示優(yōu)先級級別
  • relative_priority: 相對于qos_class的相對優(yōu)先級倾芝,qos_class用于區(qū)分大的優(yōu)先級級別悬襟,relative_priority表示大級別下的小級別。relative_priority必須大于QOS_MIN_RELATIVE_PRIORITY小于0割捅,否則將返回NULL。從GCD源碼中可以查到QOS_MIN_RELATIVE_PRIORITY等于-15

使用dispatch_queue_attr_make_with_qos_class創(chuàng)建隊列時莫瞬,需要注意,非法的參數(shù)可能導(dǎo)致dispatch_queue_attr_make_with_qos_class返回NULL,dispatch_queue_create傳入NULL會創(chuàng)建出串行隊列拐袜。寫代碼過程中需要確保這是否是預(yù)期的結(jié)果

設(shè)置目標隊列(2.25日更新,感謝@楊蕭玉HIT 指出問題甜攀,原文章有誤給大家致歉)

除了通過dispatch_queue_attr_make_with_qos_class設(shè)置隊列的優(yōu)先級之外,也可以使用設(shè)置目標隊列的方法,設(shè)置隊列的優(yōu)先級彤敛。當隊列創(chuàng)建時未設(shè)置優(yōu)先級玄糟,隊列將繼承目標隊列的優(yōu)先級。(不過一般情況下還是推薦使用dispatch_queue_attr_make_with_qos_class設(shè)置隊列的優(yōu)先級)

void dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);

調(diào)用dispatch_set_target_queue會retain新目標隊列queue,release原有目標隊列贰军。設(shè)置目標隊列之后,block將會在目標隊列中執(zhí)行。注意:當目標隊列串行時童太,任何在目標隊列中執(zhí)行的block都會串行執(zhí)行翘贮,無論原隊列是否串行

假設(shè)有隊列A锨能、B是并行隊列,C為串行隊列倔约。A,B的目標隊列均設(shè)置為C钾军,那么A、B樱哼、C中的block在設(shè)置目標隊列之后最終都會串行執(zhí)行

target.png

例:
隊列1并行惨篱,隊列2串行

dispatch_queue_t queue1 = dispatch_queue_create("com.company.queue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("com.company.queue2", DISPATCH_QUEUE_SERIAL);

dispatch_async(queue1, ^{   // block1
    for (int i = 0; i < 5; i ++) {
        NSLog(@"+++++");
    }
});
dispatch_async(queue1, ^{ // block2
    for (int i = 0; i < 5; i ++) {
        NSLog(@"=====");
    }
});
dispatch_async(queue2, ^{   // block3
    for (int i = 0; i < 5; i ++) {
        NSLog(@"----");
    }
});

運行一下可知block1,block2,block3并行執(zhí)行

2016-02-25 15:05:20.024 TGCD[1940:99120] +++++
2016-02-25 15:05:20.024 TGCD[1940:99122] =====
2016-02-25 15:05:20.024 TGCD[1940:99121] ----
2016-02-25 15:05:20.025 TGCD[1940:99120] +++++
2016-02-25 15:05:20.025 TGCD[1940:99121] ----
2016-02-25 15:05:20.025 TGCD[1940:99122] =====
2016-02-25 15:05:20.025 TGCD[1940:99120] +++++
2016-02-25 15:05:20.025 TGCD[1940:99121] ----
2016-02-25 15:05:20.025 TGCD[1940:99122] =====
2016-02-25 15:05:20.025 TGCD[1940:99120] +++++
2016-02-25 15:05:20.025 TGCD[1940:99121] ----
2016-02-25 15:05:20.025 TGCD[1940:99122] =====
2016-02-25 15:05:20.025 TGCD[1940:99120] +++++
2016-02-25 15:05:20.025 TGCD[1940:99121] ----
2016-02-25 15:05:20.025 TGCD[1940:99122] =====

如果將隊列1的目標隊列設(shè)置為隊列2,會發(fā)生什么情況呢?

dispatch_queue_t queue1 = dispatch_queue_create("com.company.queue1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t queue2 = dispatch_queue_create("com.company.queue2", DISPATCH_QUEUE_SERIAL);

dispatch_set_target_queue(queue1, queue2);

dispatch_async(queue1, ^{
    for (int i = 0; i < 5; i ++) {
        NSLog(@"+++++");
    }
});
dispatch_async(queue1, ^{
    for (int i = 0; i < 5; i ++) {
        NSLog(@"=====");
    }
});
dispatch_async(queue2, ^{
    for (int i = 0; i < 5; i ++) {
        NSLog(@"----");
    }
});

block1克胳,block2捏雌,block3變?yōu)榱舜?/p>

2016-02-25 15:06:57.215 TGCD[1974:100675] +++++
2016-02-25 15:06:57.215 TGCD[1974:100675] +++++
2016-02-25 15:06:57.215 TGCD[1974:100675] +++++
2016-02-25 15:06:57.215 TGCD[1974:100675] +++++
2016-02-25 15:06:57.216 TGCD[1974:100675] +++++
2016-02-25 15:06:57.216 TGCD[1974:100675] =====
2016-02-25 15:06:57.216 TGCD[1974:100675] =====
2016-02-25 15:06:57.216 TGCD[1974:100675] =====
2016-02-25 15:06:57.216 TGCD[1974:100675] =====
2016-02-25 15:06:57.216 TGCD[1974:100675] =====
2016-02-25 15:06:57.216 TGCD[1974:100675] ----
2016-02-25 15:06:57.216 TGCD[1974:100675] ----
2016-02-25 15:06:57.216 TGCD[1974:100675] ----
2016-02-25 15:06:57.217 TGCD[1974:100675] ----
2016-02-25 15:06:57.217 TGCD[1974:100675] ----

注意不要循環(huán)設(shè)置目標隊列,如A的目標隊列為B,B的目標隊列為A宵荒。這將會導(dǎo)致無法預(yù)知的錯誤

延時

GCD中有2個延時的API

dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);
void dispatch_after_f(dispatch_time_t when, dispatch_queue_t queue, void *context, dispatch_function_t work);

一定時間之后將block加入到queue中。when用于表示時間继低,如果傳入DISPATCH_TIME_NOW會等同于dispatch_async。另外不允許傳入DISPATCH_TIME_FOREVER,這會永遠阻塞線程焙压。

通前面其他方法類似。dispatch_after接收block作為參數(shù),系統(tǒng)持有block绰沥,block中self不需要weak。dispatch_after_f接收work函數(shù)作為參數(shù),context作為work函數(shù)的第一個參數(shù)

需要注意的是這里的延時是不精確的,因為加入隊列不一定會立即執(zhí)行。延時1s可能會1.5s甚至2s之后才會執(zhí)行。

dispatch_barrier

在并行隊列中鸭限,有的時候我們需要讓某個任務(wù)單獨執(zhí)行兜喻,也就是他執(zhí)行的時候不允許其他任務(wù)執(zhí)行。這時候dispatch_barrier就派上了用場。

使用dispatch_barrier將任務(wù)加入到并行隊列之后晶姊,任務(wù)會在前面任務(wù)全部執(zhí)行完成之后執(zhí)行扒接,任務(wù)執(zhí)行過程中,其他任務(wù)無法執(zhí)行们衙,直到barrier任務(wù)執(zhí)行完成

dispatch_barrier在GCD中有4個API

void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work);
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);
void dispatch_barrier_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work); 

如果API在串行隊列中調(diào)用珠增,將等同于dispatch_async砍艾、dispatch_async_f蒂教、dispatch_syncdispatch_sync_f脆荷,不會有任何影響凝垛。

dispatch_barrier最典型的使用場景是讀寫問題,NSMutableDictionary在多個線程中如果同時寫入蜓谋,或者一個線程寫入一個線程讀取梦皮,會發(fā)生無法預(yù)料的錯誤。但是他可以在多個線程中同時讀取桃焕。如果多個線程同時使用同一個NSMutableDictionary剑肯。怎樣才能保護NSMutableDictionary不發(fā)生意外呢?

- (void)setObject:(id)anObject forKey:(id<NSCopying>)aKey
{
    dispatch_barrier_async(self.concurrentQueue, ^{
        [self.mutableDictionary setObject:anObject forKey:aKey];
    });
}

- (id)objectForKey:(id)aKey
{
    __block id object = nil;
    dispatch_sync(self.concurrentQueue, ^{
        object = [self.mutableDictionary objectForKey:aKey];
    });
    return  object;
}

當NSMutableDictionary寫入的時候观堂,我們使用dispatch_barrier_async让网,讓其單獨執(zhí)行寫入操作,不允許其他寫入操作或者讀取操作同時執(zhí)行师痕。當讀取的時候溃睹,我們只需要直接使用dispatch_sync,讓其正常讀取即可胰坟。這樣就可以保證寫入時不被打擾因篇,讀取時可以多個線程同時進行

set_specific & get_specific

有時候我們需要將某些東西關(guān)聯(lián)到隊列上,比如我們想在某個隊列上存一個東西,或者我們想?yún)^(qū)分2個隊列竞滓。GCD提供了dispatch_queue_set_specific方法咐吼,通過key,將context關(guān)聯(lián)到queue上

void dispatch_queue_set_specific(dispatch_queue_t queue, const void *key, void *context, dispatch_function_t destructor);
  • queue:需要關(guān)聯(lián)的queue商佑,不允許傳入NULL
  • key:唯一的關(guān)鍵字
  • context:要關(guān)聯(lián)的內(nèi)容锯茄,可以為NULL
  • destructor:釋放context的函數(shù),當新的context被設(shè)置時莉御,destructor會被調(diào)用

有存就有取撇吞,將context關(guān)聯(lián)到queue上之后,可以通過dispatch_queue_get_specific或者dispatch_get_specific方法將值取出來礁叔。

void *dispatch_queue_get_specific(dispatch_queue_t queue, const void *key);
void *dispatch_get_specific(const void *key);
  • dispatch_queue_get_specific: 根據(jù)queue和key取出context牍颈,queue參數(shù)不能傳入全局隊列
  • dispatch_get_specific: 根據(jù)唯一的key取出當前queue的context。如果當前queue沒有key對應(yīng)的context琅关,則去queue的target queue取煮岁,取不著返回NULL,如果對全局隊列取涣易,也會返回NULL

iOS6之后dispatch_get_current_queue()被廢棄(廢棄的原因這里不多解釋画机,如果想了解可以看這里),如果我們需要區(qū)分不同的queue新症,可以使用set_specific方法步氏。根據(jù)對應(yīng)的key是否有值來區(qū)分

END

節(jié)后第一彈,queue相關(guān)的內(nèi)容就介紹到這里徒爹,GCD的東西挺多荚醒,其他東西之后如果有時間我會慢慢介紹,敬請期待

About Me

個人博客 簡書 微博 QQ群:159974494

歡迎大家關(guān)注我隆嗅,共同學習iOS~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末界阁,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子胖喳,更是在濱河造成了極大的恐慌泡躯,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件丽焊,死亡現(xiàn)場離奇詭異较剃,居然都是意外死亡,警方通過查閱死者的電腦和手機粹懒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門重付,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人凫乖,你說我怎么就攤上這事。” “怎么了帽芽?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵删掀,是天一觀的道長。 經(jīng)常有香客問我导街,道長披泪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任搬瑰,我火速辦了婚禮款票,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泽论。我一直安慰自己艾少,他們只是感情好,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布翼悴。 她就那樣靜靜地躺著缚够,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鹦赎。 梳的紋絲不亂的頭發(fā)上谍椅,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音古话,去河邊找鬼雏吭。 笑死,一個胖子當著我的面吹牛陪踩,可吹牛的內(nèi)容都是我干的杖们。 我是一名探鬼主播,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了推盛?” 一聲冷哼從身側(cè)響起胚泌,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎单山,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡荆秦,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了力图。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片步绸。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖吃媒,靈堂內(nèi)的尸體忽然破棺而出瓤介,到底是詐尸還是另有隱情吕喘,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布刑桑,位于F島的核電站氯质,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏祠斧。R本人自食惡果不足惜闻察,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望琢锋。 院中可真熱鬧辕漂,春花似錦、人聲如沸吴超。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽烛芬。三九已至隧期,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間赘娄,已是汗流浹背仆潮。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留遣臼,地道東北人性置。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像揍堰,于是被迫代替她去往敵國和親鹏浅。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容