一、相關(guān)概念
GCD全稱Grand Central Dispatch,屬于系統(tǒng)級(jí)的線程管理,通過 GCD,我們可以對(duì)當(dāng)前程序執(zhí)行的線程進(jìn)行調(diào)度與控制,而不需要過度關(guān)注線程創(chuàng)建釋放相關(guān)內(nèi)容,這無疑大大節(jié)約了開發(fā)的精力,并且將繁瑣的線程抽象起來,更有利于掌握和理解;
- 串行(Serial):讓任務(wù)一個(gè)接著一個(gè)地執(zhí)行(一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù))
- 并發(fā)(Concurrent):可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行(自動(dòng)開啟多個(gè)線程同時(shí)執(zhí)行任務(wù))并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效。
- 同步(Synchronous):在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
- 異步(Asynchronous):在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
二、舉例說明
沒有什么比直接使用例子更通俗易懂:
隊(duì)列
分為串行隊(duì)列與并行隊(duì)列,執(zhí)行分為異步與同步
//串行隊(duì)列的創(chuàng)建方法
dispatch_queue_t queueSerial = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
//并發(fā)隊(duì)列的創(chuàng)建方法
dispatch_queue_t queueC = dispatch_queue_create("conTest.queue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"asyncConcurrent---begin");
//同步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_sync(queueC, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1---sync---%@",[NSThread currentThread]);
}
});
dispatch_sync(queueC, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2---sync---%@",[NSThread currentThread]);
}
});
dispatch_sync(queueC, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3---sync---%@",[NSThread currentThread]);
}
});
//異步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_async(queueC, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_async(queueC, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_async(queueC, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"syncConcurrent---end");
//并發(fā)同步隊(duì)列在一個(gè)線程中執(zhí)行,并發(fā)異步隊(duì)列則由系統(tǒng)分配的線程執(zhí)行,執(zhí)行速度不一定比當(dāng)前線程的速度慢
主線程與線程切換
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"asyncMain---begin");
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_block_t block = ^{
NSLog(@"block------%@",[NSThread currentThread]);
NSLog(@"new block message");
};
dispatch_async(queue, block);
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"asyncMain---end");
//在指定線程中執(zhí)行的異步操作,遵循代碼執(zhí)行順序,碰到異步的函數(shù)塊,即拋到線程最后排隊(duì);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_async( dispatch_get_main_queue(), ^{
});
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
//回到主線程
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
});
dispatch_once用法
// 使用dispatch_once構(gòu)建單例,可以保證單例線程安全
+ (instancetype)shareInstance {
static PhotoHandler *handler = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
handler = [[PhotoHandler alloc] init];
});
return handler;
}
dispatch_barrier 與dispatch_group 及 dispatch_apply 常用方法及區(qū)別
dispatch_group在日常開發(fā)中用處可能更大一點(diǎn)
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//執(zhí)行耗時(shí)操作1
NSLog(@"執(zhí)行耗時(shí)操作1");
NSLog(@"執(zhí)行耗時(shí)操作1------%@",[NSThread currentThread]);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (long i = 0 ; i < 7000000; i ++) {
long j = i;
}
NSLog(@"內(nèi)部在異步1");
NSLog(@"內(nèi)部在異步1------%@",[NSThread currentThread]);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"內(nèi)部在異步3");
NSLog(@"內(nèi)部在異步3------%@",[NSThread currentThread]);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1); //這里線程睡眠1秒鐘,模擬異步請求
NSLog(@"內(nèi)部在異步2");
NSLog(@"內(nèi)部在異步2------%@",[NSThread currentThread]);
});
});
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//執(zhí)行耗時(shí)操作2
NSLog(@"執(zhí)行耗時(shí)操作2------%@",[NSThread currentThread]);
NSLog(@"執(zhí)行耗時(shí)操作2");
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//執(zhí)行耗時(shí)操作3
NSLog(@"執(zhí)行耗時(shí)操作3------%@",[NSThread currentThread]);
NSLog(@"執(zhí)行耗時(shí)操作3");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"前面的異步操作已完成");
});
//dispatch_group_create()可以創(chuàng)建一個(gè)完全的線程控制,這這個(gè)group中的線程,無論該線程是否新開異步線程,
//dispatch_group_notify都會(huì)在該group線程所有內(nèi)容執(zhí)行完成以后,再執(zhí)行相關(guān)內(nèi)容(但是sleep(1); 模擬異步請求,dispatch_group_notify不會(huì)等待 如果要精確確定異步的執(zhí)行完畢,可以用dispatch_group_enter, dispatch_group_leave來管理)
//所謂異步執(zhí)行就是將當(dāng)前在異步執(zhí)行的代碼以函數(shù)塊形式排隊(duì)放到線程(系統(tǒng)分配的線程,不一定是目前執(zhí)行的線程)執(zhí)行的最后
//由于執(zhí)行的線程不一致,所以完成先后順序也不一致
//對(duì)于內(nèi)部再次異步的的內(nèi)容(如網(wǎng)絡(luò)請求),group并不能保證獲取結(jié)果以后再執(zhí)行通知
dispatch_barrier 與dispatch_apply
//GCD的快速迭代方法
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/*! dispatch_apply函數(shù)說明
*
* @brief dispatch_apply函數(shù)是dispatch_sync函數(shù)和Dispatch Group的關(guān)聯(lián)API
* 該函數(shù)按指定的次數(shù)將指定的Block追加到指定的Dispatch Queue中,并等到全部的處理執(zhí)行結(jié)束
*
* @param 6 指定重復(fù)次數(shù) 指定6次
* @param queue 追加對(duì)象的Dispatch Queue
* @param index 帶有參數(shù)的Block, index的作用是為了按執(zhí)行的順序區(qū)分各個(gè)Block
*
*/
dispatch_apply(6, globalQueue, ^(size_t index) {
NSLog(@"%zd---globalQueue---%@",index, [NSThread currentThread]);
});
dispatch_queue_t queue = dispatch_queue_create("ssss", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"內(nèi)部在異步1");
NSLog(@"內(nèi)部在異步1------%@",[NSThread currentThread]);
});
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"內(nèi)部在異步2");
NSLog(@"內(nèi)部在異步2------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"內(nèi)部queue------%@",[NSThread currentThread]);
}
});
});
//先執(zhí)行完柵欄前面的在執(zhí)行后面的
dispatch_barrier_sync(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
/**
1.柵欄操作時(shí)候,只能攔截該線程中第一層異步操作的內(nèi)容,對(duì)第一層中再次異步操作的線程無法攔截,同時(shí)屬于這個(gè)線程的也不行
*/
// dispatch_async(queue, ^{
// for (int i = 0; i < 2; ++i) {
// NSLog(@"3------%@",[NSThread currentThread]);
// }
// });
// dispatch_async(queue, ^{
// for (int i = 0; i < 2; ++i) {
// NSLog(@"4------%@",[NSThread currentThread]);
// }
// });
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"only once");
});
//延時(shí)執(zhí)行,不受柵欄的影響
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"run -----");
});
死鎖相關(guān)情形
-(void)deadThread {
NSLog(@"=================4");
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"=================5");
});
NSLog(@"=================6");
}
-(void)deadThread2 {
dispatch_queue_t queue = dispatch_queue_create("com.demo.serialQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"1"); // 任務(wù)1
dispatch_async(queue, ^{
NSLog(@"2"); // 任務(wù)2
dispatch_sync(queue, ^{
NSLog(@"3"); // 任務(wù)3
});
NSLog(@"4"); // 任務(wù)4
});
NSLog(@"5"); // 任務(wù)5
/**
執(zhí)行任務(wù)1;
遇到異步線程,將【任務(wù)2、同步線程、任務(wù)4】加入串行隊(duì)列中。因?yàn)槭钱惒骄€程,所以在主線程中的任務(wù)5不必等待異步線程中的所有任務(wù)完成;
因?yàn)槿蝿?wù)5不必等待,所以2和5的輸出順序不能確定;
任務(wù)2執(zhí)行完以后,遇到同步線程,這時(shí),將任務(wù)3加入串行隊(duì)列;
又因?yàn)槿蝿?wù)4比任務(wù)3早加入串行隊(duì)列,所以,任務(wù)3要等待任務(wù)4完成以后,才能執(zhí)行。但是任務(wù)3所在的同步線程會(huì)阻塞,所以任務(wù)4必須等任務(wù)3執(zhí)行完以后再執(zhí)行。這就又陷入了無限的等待中,造成死鎖。
*/
}
semaphore信號(hào)量的應(yīng)用
信號(hào)量使用的主要三個(gè)函數(shù)
//創(chuàng)建信號(hào)量,參數(shù):信號(hào)量的初值,如果小于0則會(huì)返回NULL
dispatch_semaphore_create(信號(hào)量值)
//等待降低信號(hào)量
dispatch_semaphore_wait(信號(hào)量,等待時(shí)間)
//提高信號(hào)量
dispatch_semaphore_signal(信號(hào)量)
其實(shí),這有點(diǎn)類似鎖機(jī)制了,只不過信號(hào)量都是系統(tǒng)幫助我們處理了,我們只需要在執(zhí)行線程之前,設(shè)定一個(gè)信號(hào)量值(這個(gè)值必須是大于或者等于0),關(guān)于這個(gè)值的理解我們可以從下面兩個(gè)例子中獲取比較準(zhǔn)確的定義;
一、模擬多線程操作時(shí)幾個(gè)任務(wù)同時(shí)進(jìn)行,所有結(jié)果完成后才進(jìn)行處理
dispatch_semaphore_t semaphoreControl = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"hoggen run task 1");
sleep(1);
NSLog(@"hoggen complete task 1");
dispatch_semaphore_signal(semaphoreControl);
});
dispatch_async(queue, ^{
NSLog(@"hoggen run task 2");
sleep(2);
NSLog(@"hoggen complete task 2");
dispatch_semaphore_signal(semaphoreControl);
});
dispatch_async(queue, ^{
NSLog(@"hoggen run task 3");
sleep(3);
NSLog(@"hoggen complete task 3");
dispatch_semaphore_signal(semaphoreControl);
});
dispatch_async(queue, ^{
NSLog(@"hoggen run task 4");
sleep(4);
NSLog(@"hoggen complete task 4");
dispatch_semaphore_signal(semaphoreControl);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphoreControl, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphoreControl, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphoreControl, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphoreControl, DISPATCH_TIME_FOREVER);
NSLog(@"=================模擬多線程操作時(shí)幾個(gè)任務(wù)同時(shí)進(jìn)行,完成后才輸出結(jié)果======================");
});
二、用于多線程,線程數(shù)量的控制
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任務(wù)1
dispatch_async(queue2, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 1");
sleep(1);
NSLog(@"complete task 1");
dispatch_semaphore_signal(semaphore);
});
//任務(wù)2
dispatch_async(queue2, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 2");
sleep(1);
NSLog(@"complete task 2");
dispatch_semaphore_signal(semaphore);
});
//任務(wù)3
dispatch_async(queue2, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 3");
sleep(1);
NSLog(@"complete task 3");
dispatch_semaphore_signal(semaphore);
});
輸出結(jié)果為:
=================semaphore信號(hào)量測試======================
hoggen run task 2
hoggen run task 1
hoggen run task 3
hoggen run task 4
run task 1
run task 2
hoggen complete task 1
complete task 1
complete task 2
run task 3
hoggen complete task 2
complete task 3
hoggen complete task 3
hoggen complete task 4
=================模擬多線程操作時(shí)幾個(gè)任務(wù)同時(shí)進(jìn)行,完成后才輸出結(jié)果======================