dispatch_set_target_queue
- 變更Dispatch Queue的執(zhí)行優(yōu)先級
- 創(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)前隊列時判斷為queueA或queueB隊列。而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)存管理)/