參考:
GCD源碼
深入理解 GCD
iOS多線程--徹底學(xué)會多線程之『GCD』
關(guān)于iOS多線程,我說,你聽,沒準(zhǔn)你就懂了
任務(wù)執(zhí)行方式(同步适荣、異步)
一般理解
1现柠、同步執(zhí)行(dispatch_sync):只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力弛矛。必須等到Block函數(shù)執(zhí)行完畢后够吩,dispatch函數(shù)才會返回。
2丈氓、異步執(zhí)行(dispatch_async):可以在新的線程中執(zhí)行任務(wù)周循,具備開啟新線程的能力。dispatch函數(shù)會立即返回, 然后Block在后臺異步執(zhí)行万俗。
高級理解
1湾笛、dispatch_sync 的實(shí)現(xiàn)略簡單一些,它不涉及線程池(因此一般都在當(dāng)前線程執(zhí)行)闰歪,而是利用與線程綁定的信號量來實(shí)現(xiàn)串行
2嚎研、dispatch_async 會把任務(wù)添加到隊(duì)列的一個(gè)鏈表中,添加完后會喚醒隊(duì)列库倘,根據(jù) vtable 中的函數(shù)指針临扮,調(diào)用 wakeup 方法。在 wakeup 方法中于樟,從線程池里取出工作線程(如果沒有就新建)公条,然后在工作線程中取出鏈表頭部指向的 block 并執(zhí)行。
任務(wù)管理方式(串行隊(duì)列迂曲、并行隊(duì)列)
1靶橱、串行隊(duì)列:按照FIFO(先進(jìn)先出)的原則,每次只能有一個(gè)任務(wù)執(zhí)行路捧,等待執(zhí)行完畢后才會執(zhí)行下一個(gè)任務(wù)
2关霸、并行隊(duì)列:按照FIFO(先進(jìn)先出)的原則,把任務(wù)拿出來執(zhí)行杰扫,不需要等待前一個(gè)任務(wù)執(zhí)行情況队寇。
外: dispatch_async 加入主隊(duì)列的任務(wù)由 runloop 處理,加入其他隊(duì)列由線程池處理
// 主隊(duì)列--串行,所有放在主隊(duì)列中的任務(wù)章姓,都會放到主線程中執(zhí)行
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// 全局隊(duì)列--并行佳遣,系統(tǒng)為我們創(chuàng)建好的一個(gè)并行隊(duì)列,使用起來與我們自己創(chuàng)建的并行隊(duì)列無本質(zhì)差別
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
// new串行隊(duì)列
dispatch_queue_t queue1 = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// new并行隊(duì)列
dispatch_queue_t queue2 = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
注意:避免使用 GCD Global隊(duì)列創(chuàng)建Runloop常駐線程
全局隊(duì)列的底層是一個(gè)線程池(線程數(shù)有限)凡伊,向全局隊(duì)列中提交的 block零渐,都會被放到這個(gè)線程池中執(zhí)行,如果線程池已滿系忙,后續(xù)再提交 block 就不會再重新創(chuàng)建線程诵盼。等待有空閑的線程在執(zhí)行任務(wù)。
所以:
避免使用 GCD Global 隊(duì)列創(chuàng)建 Runloop 常駐線程,如果n條線程都被霸占了风宁,Global隊(duì)列就費(fèi)了洁墙。
dispatch_group (組)
dispatch_group 的本質(zhì)就是一個(gè) value 非常大的信號量,等待 group 完成實(shí)際上就是等待 value 恢復(fù)初始值戒财。而 notify 的作用是將所有注冊的回調(diào)組裝成一個(gè)鏈表热监,在 dispatch_async 完成時(shí)判斷 value 是不是恢復(fù)初始值,如果是則調(diào)用 dispatch_async 異步執(zhí)行所有注冊的回調(diào)固翰。
dispatch_once (單次)
dispatch_once 通過一個(gè)靜態(tài)變量來標(biāo)記 block 是否已被執(zhí)行狼纬,同時(shí)使用信號量確保只有一個(gè)線程能執(zhí)行,執(zhí)行完 block 后會喚醒其他所有等待的線程
dispatch_barrier_async (柵欄)
dispatch_barrier_async 改變了 block 的 vtable 標(biāo)記位骂际,當(dāng)它將要被取出執(zhí)行時(shí)疗琉,會等待前面的 block 都執(zhí)行完,然后在下一次循環(huán)中被執(zhí)行
任務(wù)+隊(duì)列
串行隊(duì)列 | 并行隊(duì)列 | 主隊(duì)列 | |
---|---|---|---|
同步(sync) | 當(dāng)前線程歉铝,串行執(zhí)行 | 隊(duì)列當(dāng)前線程盈简,串行執(zhí)行 | 主線程,串行執(zhí)行(注意死鎖) |
異步(async) | 開1條新線程太示,串行執(zhí)行 | 開n條新線程柠贤,異步執(zhí)行(n在iphone7上面最大是幾十個(gè)) | 主線程,串行執(zhí)行 |
判斷當(dāng)前隊(duì)列
static char *queueKey = "queueKey";
dispatch_queue_t queue1 = dispatch_queue_create(queueKey, nil);
dispatch_queue_set_specific(queue, queueKey, &queueKey, NULL); // 設(shè)置標(biāo)識
dispatch_sync(queue1, ^{
if (dispatch_get_specific(queueKey)) {
//說明當(dāng)前的隊(duì)列就是queue1
}else{
//說明當(dāng)前的隊(duì)列不是是queue1
}
});
容易誤解的概念
1类缤、主線程只會執(zhí)行主隊(duì)列的任務(wù)--(主線程也可以執(zhí)行其他隊(duì)列臼勉,比如sync執(zhí)行其他隊(duì)列)
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_queue_set_specific(mainQueue, "key", "main", NULL);
dispatch_sync(globalQueue, ^{
BOOL res1 = [NSThread isMainThread];
BOOL res2 = dispatch_get_specific("key") != NULL;
NSLog(@"is main thread: %zd --- is main queue: %zd", res1, res2);
});
根據(jù)正常邏輯的理解來說,這里的兩個(gè)判斷結(jié)果應(yīng)該都是NO餐弱,但運(yùn)行后宴霸,第一個(gè)判斷為YES,后者為NO膏蚓,輸出說明了主線程此時(shí)執(zhí)行了work queue的任務(wù).
Dispatch Block
隊(duì)列執(zhí)行任務(wù)都是block的方式瓢谢,
創(chuàng)建block
- (void)createDispatchBlock {
// 一般的block
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.starming.gcddemo.concurrentqueue",DISPATCH_QUEUE_CONCURRENT);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"run block");
});
dispatch_async(concurrentQueue, block);
//QOS優(yōu)先級的block
dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_USER_INITIATED, -1, ^{
NSLog(@"run qos block");
});
dispatch_async(concurrentQueue, qosBlock);
}
dispatch_block_wait:可以根據(jù)dispatch block來設(shè)置等待時(shí)間,參數(shù)DISPATCH_TIME_FOREVER會一直等待block結(jié)束
- (void)dispatchBlockWaitDemo {
dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t block = dispatch_block_create(0, ^{
NSLog(@"star");
[NSThread sleepForTimeInterval:5.f];
NSLog(@"end");
});
dispatch_async(serialQueue, block);
//設(shè)置DISPATCH_TIME_FOREVER會一直等到前面任務(wù)都完成
dispatch_block_wait(block, DISPATCH_TIME_FOREVER);
NSLog(@"ok, now can go on");
}
dispatch_block_notify:可以監(jiān)視指定dispatch block結(jié)束驮瞧,然后再加入一個(gè)block到隊(duì)列中氓扛。三個(gè)參數(shù)分別為,第一個(gè)是需要監(jiān)視的block论笔,第二個(gè)參數(shù)是需要提交執(zhí)行的隊(duì)列采郎,第三個(gè)是待加入到隊(duì)列中的block
- (void)dispatchBlockNotifyDemo {
dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t firstBlock = dispatch_block_create(0, ^{
NSLog(@"first block start");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"first block end");
});
dispatch_async(serialQueue, firstBlock);
dispatch_block_t secondBlock = dispatch_block_create(0, ^{
NSLog(@"second block run");
});
//first block執(zhí)行完才在serial queue中執(zhí)行second block
dispatch_block_notify(firstBlock, serialQueue, secondBlock);
}
dispatch_block_cancel:iOS8之后可以調(diào)用dispatch_block_cancel來取消(需要注意必須用dispatch_block_create創(chuàng)建dispatch_block_t)
需要注意的是,未執(zhí)行的可以用此方法cancel掉狂魔,若已經(jīng)執(zhí)行則cancel不了
如果想中斷(interrupt)線程尉剩,可以使用dispatch_block_testcancel方法
- (void)dispatchBlockCancelDemo {
dispatch_queue_t serialQueue = dispatch_queue_create("com.starming.gcddemo.serialqueue", DISPATCH_QUEUE_SERIAL);
dispatch_block_t firstBlock = dispatch_block_create(0, ^{
NSLog(@"first block start");
[NSThread sleepForTimeInterval:2.f];
NSLog(@"first block end");
});
dispatch_block_t secondBlock = dispatch_block_create(0, ^{
NSLog(@"second block run");
});
dispatch_async(serialQueue, firstBlock);
dispatch_async(serialQueue, secondBlock);
//取消secondBlock
dispatch_block_cancel(secondBlock);
}
1. 串行隊(duì)列 + 同步執(zhí)行
不會開啟新線程,在當(dāng)前線程執(zhí)行任務(wù)毅臊。任務(wù)是串行的,執(zhí)行完一個(gè)任務(wù),再執(zhí)行下一個(gè)任務(wù)
- (void)serialQueueSync{
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t serialQueue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_sync(serialQueue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog輸出
0========<NSThread: 0x60800007f840>{number = 3, name = (null)}
1========<NSThread: 0x60800007f840>{number = 3, name = (null)}
2========<NSThread: 0x60800007f840>{number = 3, name = (null)}
3========<NSThread: 0x60800007f840>{number = 3, name = (null)}
4========<NSThread: 0x60800007f840>{number = 3, name = (null)}
*/
}
2. 串行隊(duì)列 + 異步執(zhí)行
開一個(gè)新線程管嬉,一個(gè)一個(gè)執(zhí)行任務(wù)
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog輸出
0========<NSThread: 0x6000002616c0>{number = 1, name = main}
4========<NSThread: 0x6000002616c0>{number = 1, name = main}
1========<NSThread: 0x608000270540>{number = 3, name = (null)}
2========<NSThread: 0x608000270540>{number = 3, name = (null)}
3========<NSThread: 0x608000270540>{number = 3, name = (null)}
*/
3. 并行隊(duì)列 + 同步執(zhí)行
當(dāng)前線程皂林,一個(gè)一個(gè)執(zhí)行任務(wù)
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog輸出
0========<NSThread: 0x60800007f840>{number = 3, name = (null)}
1========<NSThread: 0x60800007f840>{number = 3, name = (null)}
2========<NSThread: 0x60800007f840>{number = 3, name = (null)}
3========<NSThread: 0x60800007f840>{number = 3, name = (null)}
4========<NSThread: 0x60800007f840>{number = 3, name = (null)}
*/
4. 并行隊(duì)列 + 異步執(zhí)行
開多個(gè)線程,異步執(zhí)行
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog輸出
0========<NSThread: 0x608000070300>{number = 1, name = main}
4========<NSThread: 0x608000070300>{number = 1, name = main}
2========<NSThread: 0x608000264140>{number = 4, name = (null)}
1========<NSThread: 0x60000007a800>{number = 3, name = (null)}
3========<NSThread: 0x6080002642c0>{number = 5, name = (null)}
*/
5. 主隊(duì)列 + 異步執(zhí)行
主線程蚯撩,同步執(zhí)行
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog輸出
0========<NSThread: 0x60000026e000>{number = 3, name = (null)}
4========<NSThread: 0x60000026e000>{number = 3, name = (null)}
1========<NSThread: 0x60000007e2c0>{number = 1, name = main}
2========<NSThread: 0x60000007e2c0>{number = 1, name = main}
3========<NSThread: 0x60000007e2c0>{number = 1, name = main}
*/
6. 主隊(duì)列 + 同步執(zhí)行 (不能在主隊(duì)列這么用础倍,死鎖)
主線程,同步執(zhí)行
NSLog(@"0========%@",[NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"2========%@",[NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"3========%@",[NSThread currentThread]);
});
NSLog(@"4========%@",[NSThread currentThread]);
/*
NSLog輸出
0========<NSThread: 0x600000263840>{number = 3, name = (null)}
1========<NSThread: 0x608000078ec0>{number = 1, name = main}
2========<NSThread: 0x608000078ec0>{number = 1, name = main}
3========<NSThread: 0x608000078ec0>{number = 1, name = main}
4========<NSThread: 0x600000263840>{number = 3, name = (null)}
*/
GCD其他用法
dispatch_after延時(shí)
1胎挎、time = 0沟启,是直接調(diào)用異步dispatch_async
2、time > 0, 只是延時(shí)提交block犹菇,不是延時(shí)執(zhí)行德迹。
//2秒延時(shí)、在主隊(duì)列
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
});
dispatch_once與dispatch_once_t
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//
});
1揭芍、dispatch_once并不是簡單的只執(zhí)行一次那么簡單
2胳搞、dispatch_once本質(zhì)上可以接受多次請求,會對此維護(hù)一個(gè)請求鏈表
3称杨、如果在block執(zhí)行期間肌毅,多次進(jìn)入調(diào)用同類的dispatch_once函數(shù)(即單例函數(shù)),會導(dǎo)致整體鏈表無限增長姑原,造成永久性死鎖悬而。
遞歸互相嵌套,如下:
- (void)viewDidLoad {
[super viewDidLoad];
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ // 鏈表無限增長
[self viewDidLoad];
});
}
dispatch_once源碼
static void
dispatch_once_f_slow(dispatch_once_t *val, void *ctxt, dispatch_function_t func)
{
#if DISPATCH_GATE_USE_FOR_DISPATCH_ONCE
dispatch_once_gate_t l = (dispatch_once_gate_t)val;
if (_dispatch_once_gate_tryenter(l)) {
_dispatch_client_callout(ctxt, func);
_dispatch_once_gate_broadcast(l);
} else {
_dispatch_once_gate_wait(l);
}
#else
_dispatch_once_waiter_t volatile *vval = (_dispatch_once_waiter_t*)val;
struct _dispatch_once_waiter_s dow = { };
_dispatch_once_waiter_t tail = &dow, next, tmp;
dispatch_thread_event_t event;
if (os_atomic_cmpxchg(vval, NULL, tail, acquire)) {
// 第一次dispatch_once,原子性操作
// 當(dāng)前線程
dow.dow_thread = _dispatch_tid_self();
// 執(zhí)行block
_dispatch_client_callout(ctxt, func);
// 第一次執(zhí)行完了锭汛,設(shè)置token = DISPATCH_ONCE_DONE
next = (_dispatch_once_waiter_t)_dispatch_once_xchg_done(val);
while (next != tail) {
// 繼續(xù)去下一個(gè)
tmp = (_dispatch_once_waiter_t)_dispatch_wait_until(next->dow_next);
event = &next->dow_event;
next = tmp;
// 信號量++
_dispatch_thread_event_signal(event);
}
} else {
// 第二次dispatch_once進(jìn)來
_dispatch_thread_event_init(&dow.dow_event);
next = *vval;
for (;;) {
if (next == DISPATCH_ONCE_DONE) { // token是否等于DISPATCH_ONCE_DONE
// 第一次執(zhí)行完之后笨奠,都是走這里
break;
}
// 如果是嵌套使用,第一次沒有完成店乐,又要執(zhí)行一次
if (os_atomic_cmpxchgv(vval, next, tail, &next, release)) {
// 原子性
dow.dow_thread = next->dow_thread;
dow.dow_next = next;
if (dow.dow_thread) {
pthread_priority_t pp = _dispatch_get_priority();
_dispatch_thread_override_start(dow.dow_thread, pp, val);
}
// 等待信號量
_dispatch_thread_event_wait(&dow.dow_event);
if (dow.dow_thread) {
_dispatch_thread_override_end(dow.dow_thread, val);
}
break;
}
}
_dispatch_thread_event_destroy(&dow.dow_event);
}
#endif
}
dispatch_apply(count,queue,block(index))迭代方法
該函數(shù)按指定的次數(shù)將指定的block追加到指定的隊(duì)列艰躺;使用的地方,阻塞當(dāng)前線程
NSLog(@"CurrentThread------%@", [NSThread currentThread]);
//dispatch_queue_t queue = dispatch_get_global_queue(0,0);
dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// 6是次數(shù)
dispatch_apply(6, queue, ^(size_t index) {
NSLog(@"%zd------%@",index, [NSThread currentThread]);
});
/*
并發(fā)隊(duì)列:開多線程異步執(zhí)行
NSLogx信息
CurrentThread------<NSThread: 0x600000268a40>{number = 3, name = (null)}
0------<NSThread: 0x600000268a40>{number = 3, name = (null)}
1------<NSThread: 0x608000266e40>{number = 4, name = (null)}
2------<NSThread: 0x608000266f00>{number = 5, name = (null)}
3------<NSThread: 0x608000266f40>{number = 6, name = (null)}
4------<NSThread: 0x600000268a40>{number = 3, name = (null)}
5------<NSThread: 0x608000266e40>{number = 4, name = (null)}
*/
/*
同步隊(duì)列:當(dāng)前線程同步執(zhí)行
NSLogx信息
CurrentThread------<NSThread: 0x608000072c00>{number = 3, name = (null)}
0------<NSThread: 0x6000000694c0>{number = 1, name = main}
1------<NSThread: 0x6000000694c0>{number = 1, name = main}
2------<NSThread: 0x6000000694c0>{number = 1, name = main}
3------<NSThread: 0x6000000694c0>{number = 1, name = main}
4------<NSThread: 0x6000000694c0>{number = 1, name = main}
5------<NSThread: 0x6000000694c0>{number = 1, name = main}
*/
dispatch_apply能避免線程爆炸眨八,因?yàn)镚CD會管理并發(fā)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 999; i++){
dispatch_async(queue, ^{
NSLog(@"%d,%@",i,[NSThread currentThread]);// 能開多大線程就開多大線程(幾十個(gè))
});
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(999, queue, ^(size_t i){
NSLog(@"%d,%@",i,[NSThread currentThread]); // 只開一定數(shù)量的線程(幾個(gè))
});
dispatch_suspend腺兴、dispatch_resume (用在dispatch_get_global_queue主隊(duì)列無效)
dispatch_suspend,dispatch_resume提供了“掛起廉侧、恢復(fù)”隊(duì)列的功能页响,簡單來說,就是可以暫停段誊、恢復(fù)隊(duì)列上的任務(wù)闰蚕。但是這里的“掛起”,并不能保證可以立即停止隊(duì)列上正在運(yùn)行的block
注意點(diǎn):
1连舍、如果隊(duì)列沒有使用dispatch_suspend没陡,使用dispatch_resume會crash
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_resume(queue); // crash
}
2、如果queue被掛起,queue銷毀時(shí)候沒有被喚醒盼玄,會crash
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_suspend(queue);// 如果queue被掛起贴彼,queue銷毀時(shí)候沒有被喚醒,會crash
3埃儿、dispatch_suspend后面執(zhí)行dispatch_sync器仗,阻塞當(dāng)前線程,需要其他線程恢復(fù)隊(duì)列
queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_suspend(queue);
// 后面執(zhí)行dispatch_sync童番,阻塞當(dāng)前線程精钮,需要其他線程恢復(fù)隊(duì)列
dispatch_sync(queue, ^{
NSLog(@"=====%@",@"111111");
});
NSLog(@"=====%@",@"22222");
GCD的隊(duì)列組dispatch_group_t,其實(shí)就是封裝了一個(gè)無限大的信號量,
注意事項(xiàng)
1剃斧、dispatch_group_async(只有async轨香,無sync)等價(jià)于{dispatch_group_enter() + async}, async調(diào)用完了會執(zhí)行dispatch_group_leave()。
2悯衬、dispatch_group_enter()就是信號量--弹沽;
3、dispatch_group_leave()就是信號量++
4筋粗、dispatch_group_enter() 必須運(yùn)行在 dispatch_group_leave() 之前策橘。
5、dispatch_group_enter() 和 dispatch_group_leave() 需要成對出現(xiàn)的
//1.創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
//2.1.全局隊(duì)列
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-01 - %@", [NSThread currentThread]);
}
});
//2.2.主隊(duì)列
dispatch_group_async(group, dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-02 - %@", [NSThread currentThread]);
}
});
//2.3.自建串行隊(duì)列
dispatch_group_async(group, dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL), ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-03 - %@", [NSThread currentThread]);
}
});
//3.都完成后會自動(dòng)通知,不阻塞當(dāng)前線程
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});
NSLog(@"當(dāng)前線程=====%@",[NSThread currentThread]);
/*
并行隊(duì)列娜亿、自建串行隊(duì)列的任務(wù)多線程異步執(zhí)行
主隊(duì)列的任務(wù)主線程同步執(zhí)行丽已,且排在全部任務(wù)的最后
NSLog信息
當(dāng)前線程=====<NSThread: 0x60000007c240>{number = 1, name = main}
group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
group-01 - <NSThread: 0x60800026d180>{number = 3, name = (null)}
group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
group-03 - <NSThread: 0x60000026c7c0>{number = 4, name = (null)}
group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
group-02 - <NSThread: 0x60000007c240>{number = 1, name = main}
完成 - <NSThread: 0x60000007c240>{number = 1, name = main}
*/
手動(dòng)標(biāo)記group完成
- dispatch_group_enter(group)
- dispatch_group_leave(group);
dispatch_group_notify(不阻塞)相當(dāng)于把block任務(wù)加在最后
NSLog(@"start");
//1.創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
for (int i=0; i< 5; i++) {
dispatch_group_enter(group); // enter
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// something
NSLog(@"something===%zd",i);
dispatch_group_leave(group); // eave
});
}
// 都完成后會自動(dòng)通知,不阻塞當(dāng)前線程
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});
NSLog(@"end");
/*
NSLog信息:
start
end
something===1
something===0
something===2
something===3
something===4
完成 - <NSThread: 0x60800006e900>{number = 1, name = main}
*/
dispatch_group_wait就是等待group的信號量回到初始值(阻塞當(dāng)前線程)
NSLog(@"start");
//1.創(chuàng)建隊(duì)列組
dispatch_group_t group = dispatch_group_create();
for (int i=0; i< 5; i++) {
dispatch_group_enter(group); // enter
dispatch_async(dispatch_get_global_queue(0, 0), ^{
// something
[NSThread sleepForTimeInterval:2];
NSLog(@"something===%zd",i);
dispatch_group_leave(group); // eave
});
}
// 阻塞當(dāng)前線程的、等待5秒
dispatch_time_t waitTime = dispatch_time(DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC);
dispatch_group_wait(group, waitTime);//特殊值有:DISPATCH_TIME_FOREVER是無限等买决,DISPATCH_TIME_NOW是不等
NSLog(@"end");
/*
等待時(shí)間 < 執(zhí)行需要時(shí)間
NSLog信息:
start
end
something===0
something===1
something===3
something===2
something===4
*/
/*
等待時(shí)間 > 執(zhí)行需要時(shí)間
NSLog信息:
start
something===1
something===0
something===4
something===2
something===3
end
*/
dispatch_semaphore_create & dispatch_semaphore_signal & dispatch_semaphore_wait
信號量是控制任務(wù)執(zhí)行的重要條件沛婴,當(dāng)信號量為0時(shí),所有任務(wù)等待督赤,信號量越大嘁灯,允許可并行執(zhí)行的任務(wù)數(shù)量越多。
- dispatch_semaphore_create(long value);創(chuàng)建信號量躲舌,初始值不能小于0丑婿;value信號數(shù)值
- dispatch_semaphore_wait(semaphore, timeout);等待降低信號量,也就是信號量-1没卸;timeout不是調(diào)用dispatch_semaphore_wait后等待的時(shí)間,而是信號量創(chuàng)建后的時(shí)間
- dispatch_semaphore_signal(semaphore);提高信號量羹奉,也就是信號量+1;
- dispatch_semaphore_wait和dispatch_semaphore_signal通常配對使用约计。
// 相當(dāng)于控制新建的線程數(shù)
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
for (int i=0; i< 10; i++) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[NSThread sleepForTimeInterval:1];
NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
}
/*
dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
只有5條線程
NSLog信息:
第0次_<NSThread: 0x608000268b00>{number = 4, name = (null)}
第1次_<NSThread: 0x600000269240>{number = 6, name = (null)}
第3次_<NSThread: 0x600000269100>{number = 5, name = (null)}
第2次_<NSThread: 0x608000268ac0>{number = 3, name = (null)}
第4次_<NSThread: 0x600000269780>{number = 7, name = (null)}
第8次_<NSThread: 0x608000268ac0>{number = 3, name = (null)}
第7次_<NSThread: 0x600000269100>{number = 5, name = (null)}
第6次_<NSThread: 0x608000268b00>{number = 4, name = (null)}
第5次_<NSThread: 0x600000269240>{number = 6, name = (null)}
第9次_<NSThread: 0x600000269780>{number = 7, name = (null)}
*/
/*
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
10條線程
NSLog信息:
第2次_<NSThread: 0x6080000661c0>{number = 3, name = (null)}
第4次_<NSThread: 0x608000073e40>{number = 7, name = (null)}
第1次_<NSThread: 0x600000079dc0>{number = 4, name = (null)}
第5次_<NSThread: 0x6000000721c0>{number = 8, name = (null)}
第3次_<NSThread: 0x608000073dc0>{number = 6, name = (null)}
第0次_<NSThread: 0x608000073d40>{number = 5, name = (null)}
第6次_<NSThread: 0x608000073d80>{number = 9, name = (null)}
第9次_<NSThread: 0x608000073e00>{number = 10, name = (null)}
第7次_<NSThread: 0x6000000717c0>{number = 11, name = (null)}
第8次_<NSThread: 0x600000066b40>{number = 12, name = (null)}
*/
dispatch_barrier_async对省、dispatch_barrier_sync (承上啟下--用于自建的并行隊(duì)列)
保證此前的任務(wù)都先于自己執(zhí)行左刽,此后的任務(wù)也遲于自己執(zhí)行商佑。
dispatch_barrier_async 不阻塞當(dāng)前線程;
dispatch_barrier_sync 阻塞當(dāng)前線程细卧;
注意:dispatch_barrier_(a)sync只在自己創(chuàng)建的并發(fā)隊(duì)列上有效,在全局(Global)并發(fā)隊(duì)列俗孝、串行隊(duì)列上酒甸,效果跟dispatch_(a)sync效果一樣。
- (void)test{
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_async(globalQueue, ^{
NSLog(@"任務(wù)1");
});
dispatch_async(globalQueue, ^{
NSLog(@"任務(wù)2");
});
dispatch_barrier_async(globalQueue, ^{
NSLog(@"任務(wù)barrier");
});
dispatch_async(globalQueue, ^{
NSLog(@"任務(wù)3");
});
dispatch_async(globalQueue, ^{
NSLog(@"任務(wù)4");
});
/*
2017-09-02 21:03:40.255 NSThreadTest[28856:21431532] 任務(wù)2
2017-09-02 21:03:40.255 NSThreadTest[28856:21431535] 任務(wù)1
2017-09-02 21:03:40.255 NSThreadTest[28856:21431533] 任務(wù)barrier
2017-09-02 21:03:40.255 NSThreadTest[28856:21431551] 任務(wù)3
2017-09-02 21:03:40.256 NSThreadTest[28856:21431550] 任務(wù)4
*/
}
GCD創(chuàng)建Timer
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
//創(chuàng)建隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//1.創(chuàng)建一個(gè)GCD定時(shí)器
/*
第一個(gè)參數(shù):表明創(chuàng)建的是一個(gè)定時(shí)器
第四個(gè)參數(shù):隊(duì)列
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 需要對timer進(jìn)行強(qiáng)引用赋铝,保證其不會被釋放掉,才會按時(shí)調(diào)用block塊
// 局部變量沽瘦,讓指針強(qiáng)引用
self.timer = timer;
//2.設(shè)置定時(shí)器的開始時(shí)間,間隔時(shí)間,精準(zhǔn)度
/*
第1個(gè)參數(shù):要給哪個(gè)定時(shí)器設(shè)置
第2個(gè)參數(shù):開始時(shí)間
第3個(gè)參數(shù):間隔時(shí)間
第4個(gè)參數(shù):精準(zhǔn)度 一般為0 在允許范圍內(nèi)增加誤差可提高程序的性能
GCD的單位是納秒 所以要*NSEC_PER_SEC
*/
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
//3.設(shè)置定時(shí)器要執(zhí)行的事情
dispatch_source_set_event_handler(timer, ^{
NSLog(@"---%@--",[NSThread currentThread]);
});
// 啟動(dòng)
dispatch_resume(timer);
}
GCD各種死鎖的情況
1革骨、最常見的(主線中+主隊(duì)列+同步)
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"任務(wù)1========%@",[NSThread currentThread]);// 當(dāng)前是主線程、主隊(duì)列
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"任務(wù)2========%@",[NSThread currentThread]);
});
NSLog(@"任務(wù)3========%@",[NSThread currentThread]);
/*
NSLog信息
任務(wù)1========<NSThread: 0x60000007c5c0>{number = 1, name = main}
解析
1.viewDidLoad是主隊(duì)列析恋,dispatch_sync也屬于主隊(duì)列良哲,
2.dispatch_sync是viewDidLoad里面的代碼,viewDidLoad需要等待dispatch_sync執(zhí)行完助隧,dispatch_sync需要等待viewDidLoad執(zhí)行完筑凫,這就死鎖了
*/
}
- (void)viewDidLoad {
[super viewDidLoad];
NSLog(@"0========%@",[NSThread currentThread]);// 當(dāng)前是主線程、主隊(duì)列
// 改成dispatch_get_global_queue或者new出來的隊(duì)列
dispatch_sync(dispatch_get_global_queue(0,0), ^{
NSLog(@"1========%@",[NSThread currentThread]);
});
NSLog(@"2========%@",[NSThread currentThread]);
/*
NSLog信息
0========<NSThread: 0x6000000690c0>{number = 1, name = main}
1========<NSThread: 0x6000000690c0>{number = 1, name = main}
2========<NSThread: 0x6000000690c0>{number = 1, name = main}
解析
1.viewDidLoad是主隊(duì)列并村,dispatch_sync是global_queue巍实,不在同一隊(duì)列
2.dispatch_sync是viewDidLoad里面的代碼,viewDidLoad需要等待dispatch_sync執(zhí)行完返回哩牍,但是dispatch_sync不需要等待viewDidLoad執(zhí)行完棚潦,立即執(zhí)行完返回
*/
}
2、串行隊(duì)列膝昆,各種嵌套異步情況
死鎖的原因:是同一個(gè)串行隊(duì)列任務(wù)內(nèi)部代碼繼續(xù)嵌套同步sync的任務(wù)
// 串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
// 同步嵌異步----執(zhí)行OK
dispatch_sync(queue, ^{
dispatch_async(queue, ^{
});
});
// 異步嵌異步----執(zhí)行OK
dispatch_async(queue, ^{
dispatch_async(queue, ^{
});
});
// 異步嵌同步----死鎖
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
});
});
// 同步嵌同步----死鎖
dispatch_sync(queue, ^{
dispatch_sync(queue, ^{
});
});
3丸边、并行隊(duì)列,各種嵌套異步情況
并行 隊(duì)列各種嵌套都不會死鎖
// 并行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
// 同步嵌異步----執(zhí)行OK
dispatch_sync(queue, ^{
dispatch_async(queue, ^{
});
});
// 異步嵌異步----執(zhí)行OK
dispatch_async(queue, ^{
dispatch_async(queue, ^{
});
});
// 異步嵌同步----執(zhí)行OK
dispatch_async(queue, ^{
dispatch_sync(queue, ^{
});
});
// 同步嵌同步----執(zhí)行OK
dispatch_sync(queue, ^{
dispatch_sync(queue, ^{
});
});
4荚孵、dispatch_apply阻塞當(dāng)前線程
// 主隊(duì)列使用妹窖,死鎖
dispatch_apply(5, dispatch_get_main_queue(), ^(size_t i) {
NSLog(@"第%@次_%@",@(i),[NSThread currentThread]);
});
// 嵌套使用,死鎖
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_apply(10, queue, ^(size_t) {
// 任務(wù)
...
dispatch_apply(10, queue, ^(size_t) {
// 任務(wù)
...
});
});
dispatch_barrier
dispatch_barrier_sync在串行隊(duì)列和全局并行隊(duì)列里面和dispatch_sync同樣的效果收叶,所以需考慮同dispatch_sync一樣的死鎖問題骄呼。
5、 信號量阻塞主線程
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(@"semaphore create!");
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_semaphore_signal(semaphore);
NSLog(@"semaphore plus 1");
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore minus 1");
}
原因:
如果當(dāng)前執(zhí)行的線程是主線程滔驾,以上代碼就會出現(xiàn)死鎖谒麦。
因?yàn)閐ispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)阻塞了當(dāng)前線程,而且等待時(shí)間是DISPATCH_TIME_FOREVER——永遠(yuǎn)等待哆致,這樣它就永遠(yuǎn)的阻塞了當(dāng)前線程——主線程绕德。導(dǎo)致主線中的dispatch_semaphore_signal(semaphore)沒有執(zhí)行,
而dispatch_semaphore_wait一直在等待dispatch_semaphore_signal改變信號量摊阀,這樣就形成了死鎖耻蛇。
解決方法:
應(yīng)該將信號量移到并行隊(duì)列中踪蹬,如全局調(diào)度隊(duì)列。以下場景臣咖,移到串行隊(duì)列也是可以的跃捣。但是串行隊(duì)列還是有可能死鎖的(如果執(zhí)行dispatch_semaphore_signal方法還是在對應(yīng)串行隊(duì)列中的話,即之前提到的串行隊(duì)列嵌套串行隊(duì)列的場景)夺蛇。
- (void)viewDidLoad {
[super viewDidLoad];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(@"semaphore create!");
dispatch_async(dispatch_get_main_queue(), ^{
dispatch_semaphore_signal(semaphore); // +1
NSLog(@"semaphore plus 1");
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"semaphore minus 1");
});
}
一些嵌套使用問題
NSLog(@"1");
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
NSLog(@"2");
dispatch_async(dispatch_get_main_queue(), ^{
sleep(1);
NSLog(@"3");
});
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"4");
});
NSLog(@"5");
//結(jié)果是:12534
//解析:“2”并發(fā)隊(duì)列同步任務(wù)疚漆,所以125;“3”刁赦、“4”是兩個(gè)主隊(duì)列異步娶聘,串行執(zhí)行任務(wù)34;最終就是12534
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queue, ^{
dispatch_sync(queue, ^{
NSLog(@"B");
});
NSLog(@"A");
});
// 結(jié)果是:BA (并發(fā)隊(duì)列不會死鎖) 并行隊(duì)列甚脉,任務(wù)A加入隊(duì)列執(zhí)行中丸升,然后任務(wù)B加入隊(duì)列也立即執(zhí)行,但是任務(wù)A會等任務(wù)B先執(zhí)行完牺氨。