Dispatch常用方法

dispatch_set_target_queue

  1. 變更Dispatch Queue的執(zhí)行優(yōu)先級
  2. 創(chuàng)建隊列層次體系
queueA = dispatch_queue_create("com.lyk.queueA", NULL);
queueB = dispatch_queue_create("com.lyk.queueB", DISPATCH_QUEUE_CONCURRENT);

// 將queueB隊列中的任務(wù)依次添加到queueA中。這樣queueB的優(yōu)先級就與queueA保持一致了。
// 而且設(shè)置了target后,在判斷當(dāng)前任務(wù)執(zhí)行隊列會出現(xiàn)點(diǎn)事情
dispatch_set_target_queue(queueB, queueA);

延伸邏輯

創(chuàng)建隊列層次體系,是什么樣的層次體系?先看一個例子

// 準(zhǔn)備工作
static void * queueuBKey = &queueuBKey;

queueA = dispatch_queue_create("com.lyk.queueA", NULL);
queueB = dispatch_queue_create("com.lyk.queueB", DISPATCH_QUEUE_CONCURRENT);

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//        dispatch_queue_set_specific(queueA, queueuAKey, queueuAKey, NULL);
    dispatch_queue_set_specific(queueB, queueuBKey, queueuBKey, NULL);
});
// 測試
dispatch_set_target_queue(queueA, queueB);

dispatch_sync(queueB, ^{
    NSLog(@"queueB-是否為B:%@", dispatch_get_specific(queueuBKey) == queueuBKey ? @"True" : @"False");
});

dispatch_sync(queueA, ^{
    NSLog(@"queueA-是否為B:%@", dispatch_get_specific(queueuBKey) == queueuBKey ? @"True" : @"False");
});

/* 打印輸出
* queueB-是否為B:True
* queueA-是否為B:True
*/
// 如果設(shè)置成這樣
dispatch_set_target_queue(queueB, queueA); 

/* 打印輸出
* queueB-是否為B:True
* queueA-是否為B:False
*/

使用dispatch_get_specific來驗證當(dāng)前隊列,我們看下其內(nèi)部實現(xiàn)原理:

void *  dispatch_get_specific(const void *key)
{
    if (slowpath(!key)) {
        return NULL;
    }

    void *ctxt = NULL;
  
    dispatch_queue_t dq = _dispatch_queue_get_current();
  
    while (slowpath(dq)) {
        if (slowpath(dq->dq_specific_q)) {
            ctxt = (void *)key;
            dispatch_sync_f(dq->dq_specific_q, &ctxt,
                    _dispatch_queue_get_specific);
            if (ctxt) break;
        }
        dq = dq->do_targetq;    // 如果當(dāng)前隊列找不到標(biāo)識,就會根據(jù)其targetq向上找
    }
    return ctxt;
}

通過上例可以發(fā)現(xiàn),上述打印效果區(qū)分:

// 相當(dāng)于queueB的targetq是queueA
dispatch_set_target_queue(queueB, queueA); 

/*以下展示目標(biāo)關(guān)系,B.do_targetq = A; A 在上游,B在下游
A NULL          ; 當(dāng)在A隊列中執(zhí)行以下代碼獲取到 NULL ,向上游找targetq結(jié)果還是找不到
B   queueuBKey; 當(dāng)在B隊列中執(zhí)行以下代碼會獲取到 queueuBKey
*/
dispatch_get_specific(queueuBKey)       // queueA,判斷不為B; queueB判斷為B
// 相當(dāng)于queueA的targetq是queueB
dispatch_set_target_queue(queueA, queueB); 

/*以下展示目標(biāo)關(guān)系,A.do_targetq = B; B 在上游,A在下游
B   queueuBKey; 當(dāng)在B隊列中執(zhí)行以下代碼會獲取到 queueuBKey
A NULL          ; 當(dāng)在A隊列中獲取為NULL,需要順著targetq向上游找,就找到B,進(jìn)而找到queueBKey
*/
dispatch_get_specific(queueuBKey);      // queueA,QueueB都可以判斷為B隊列

總結(jié)來說:dispatch_set_target_queue(queueA, queueB);會將queueA隊列中的任務(wù)提交到queueB中執(zhí)行;

所以在queueA中的任務(wù)在執(zhí)行過程中通過dispatch_get_specific判斷當(dāng)前隊列時判斷為queueAqueueB隊列。而queueB中的任務(wù)在執(zhí)行中判斷當(dāng)前隊列時只能判斷為queueB。

查找當(dāng)前隊列,如果查找不到會順著do_targetq向上游查找。


dispatch_after

3s后將任務(wù)塊添加到MainDispatchQueue中;注意不是在指定時間后執(zhí)行處理,只是在指定的時間追加操作到指定隊列中。

dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{
    NSLog(@"waited at least three seconds.");
});

dispatch_group

用來創(chuàng)建任務(wù)組,當(dāng)任務(wù)組執(zhí)行完畢后再去執(zhí)行其他任務(wù)

常用方法:

/*永久等待:第二個參數(shù)指定為等待的時間(超時),此處意味著永久等待。
只要屬于 Dispatch Group 的處理尚未執(zhí)行結(jié)束,就會一直等待,中途不能取消*/
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

/*等待指定時間: 注意超時時間的入?yún)?/
long timeout = dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, 2*NSEC_PER_SEC));
    NSLog(@"是否執(zhí)行超時:%@",timeout == 0 ? @"沒有超時":[NSString stringWithFormat:@"超時%.2fs",timeout/1000.0f]);
 /*用來監(jiān)聽隊列中的任務(wù)已全部執(zhí)行完畢,任務(wù)塊將會放到queue隊列中執(zhí)行*/
 dispatch_group_notify(group, queue, ^{NSLog(@"done");});
使用方式

以下兩種方式使用起來效果一致,但是如果block內(nèi)部又涉及到異步隊列操作問題,可能會碰到第一種方法解決不了的情況,此時就需要考慮使用第二種方式來保證順序了。

1. 通過調(diào)度組關(guān)聯(lián)隊列的方式,將一個block添加到隊列中并且與一個組進(jìn)行關(guān)聯(lián),當(dāng)任務(wù)執(zhí)行完畢
void dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block);

2. 通過自己嵌入隊列中,需要注意是手動關(guān)聯(lián)隊列,enter與leave必須要保持平衡;而且可以關(guān)聯(lián)多個組
dispatch_group_enter(group);
dispatch_async(queue1, ^{
    NSLog(@"測試4,%@",[NSThread currentThread]);
    dispatch_group_leave(group);
});
block內(nèi)部涉及隊列導(dǎo)致group保證不了順序的情況

需要注意,當(dāng)我們通過dispatch_group_async添加一個任務(wù)指的是將任務(wù)添加到隊列,同時關(guān)聯(lián)組,當(dāng)執(zhí)行完畢這個任務(wù)就通知調(diào)度組,它并去去管你這個隊列內(nèi)部做了什么操作(是去執(zhí)行任務(wù),還是將任務(wù)添加到其他隊列)。

dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("con.song.serial", DISPATCH_QUEUE_CONCURRENT);

dispatch_group_async(group, queue, ^{
    // dispatch_group_enter(group);             // 打開注釋可以解決notify通知不到的問題
    dispatch_async(dispatch_get_global_queue(0, 0), ^{  
        // 執(zhí)行一些任務(wù)塊
        sleep(3);
        NSLog(@"測試1,%@",[NSThread currentThread]);
        // dispatch_group_leave(group);
    });
});
dispatch_group_async(group, queue, ^{
    // 執(zhí)行一些任務(wù)塊
    NSLog(@"測試2,%@",[NSThread currentThread]);
});

dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"執(zhí)行任務(wù)結(jié)束:%@",[NSThread currentThread]);
});

// 打印結(jié)果(notify不是最后打印的)
QueueTest[51292:1686388] 測試2,<NSThread: 0x600003e8f980>{number = 6, name = (null)}
QueueTest[51292:1686192] 執(zhí)行任務(wù)結(jié)束:<NSThread: 0x600003ec4f00>{number = 1, name = main}
QueueTest[51292:1686392] 測試1,<NSThread: 0x600003e8ed40>{number = 3, name = (null)}

dispatch_barrier_async

只能用自己創(chuàng)建的并行隊列來操作

添加?xùn)艡诓僮骱?,會等待已?jīng)追加到隊列中的block全部執(zhí)行完畢。


dispatch_sync

同步執(zhí)行任務(wù),如果在串行隊列中執(zhí)行容易導(dǎo)致死鎖問題,注意處理。


dispatch_apply

是dispatch_sync函數(shù)和dispatch_group的關(guān)聯(lián)的API。該函數(shù)按照指定的次數(shù)將指定的block追加到指定的dispatch_queue中,病等待全部操作執(zhí)行結(jié)束。

dispatch_queue_t queue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(3, queue, ^(size_t index) {
    NSLog(@"%zu", index);
});
NSLog(@"done");

dispatch_suspendispatch / dispatch_resume

掛起指定的隊列/恢復(fù)指定的隊列。

這些函數(shù)對已經(jīng)執(zhí)行的處理沒有影響。

掛起后,追加到Dispatch Queue中但尚未執(zhí)行的操作在此之后停止執(zhí)行。

而恢復(fù)則使得這些處理能繼續(xù)執(zhí)行。


dispatch_semaphore

持有計數(shù)的信號,該計數(shù)是多線程中的計數(shù)類型信號。

可以用來做任務(wù)同步,比如講異步網(wǎng)絡(luò)請求通過這種方案設(shè)計為同步請求操作。

// 創(chuàng)建一個信號 v = 1
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

// 執(zhí)行一個任務(wù)會將信號量 -1; 注意如果信號量<1此時會進(jìn)入等待,可以設(shè)置等待超時時間
// 如果返回值為0說明正常操作,如果返回值不為0則代表超時時間
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

// 釋放一個信號會將信號量 +1
dispatch_semaphore_signal(semaphore);


dispatch_once

保證應(yīng)用程序執(zhí)行中只執(zhí)行一次指定的邏輯

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    // 初始化操作
});

dispatchI/O

適用場景:適合文章類的讀取操作(PDF,md,work,txt),因為串行隊列,所以讀取的文章是順序的,在實際中使用更多。


參考:

http://www.itdecent.cn/p/33d6f52fe26b
[https://raykle.github.io/2016/08/16/《iOS%20與%20OS%20X%20多線程和內(nèi)存管理》讀書筆記之%20GCD(二)/](iOS與OS X多線程和內(nèi)存管理)/

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

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