一、相關(guān)概念
GCD全稱Grand Central Dispatch,屬于系統(tǒng)級的線程管理,通過 GCD,我們可以對當前程序執(zhí)行的線程進行調(diào)度與控制,而不需要過度關(guān)注線程創(chuàng)建釋放相關(guān)內(nèi)容,這無疑大大節(jié)約了開發(fā)的精力,并且將繁瑣的線程抽象起來,更有利于掌握和理解;
- 串行(Serial):讓任務一個接著一個地執(zhí)行(一個任務執(zhí)行完畢后,再執(zhí)行下一個任務)
- 并發(fā)(Concurrent):可以讓多個任務并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務)并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效。
- 同步(Synchronous):在當前線程中執(zhí)行任務,不具備開啟新線程的能力
- 異步(Asynchronous):在新的線程中執(zhí)行任務,具備開啟新線程的能力
二、舉例說明
沒有什么比直接使用例子更通俗易懂:
隊列
分為串行隊列與并行隊列,執(zhí)行分為異步與同步
//串行隊列的創(chuàng)建方法
dispatch_queue_t queueSerial = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
//并發(fā)隊列的創(chuàng)建方法
dispatch_queue_t queueC = dispatch_queue_create("conTest.queue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"asyncConcurrent---begin");
//同步執(zhí)行任務創(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í)行任務創(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ā)同步隊列在一個線程中執(zhí)行,并發(fā)異步隊列則由系統(tǒng)分配的線程執(zhí)行,執(zhí)行速度不一定比當前線程的速度慢
主線程與線程切換
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ù)塊,即拋到線程最后排隊;
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ā)中用處可能更大一點
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//執(zhí)行耗時操作1
NSLog(@"執(zhí)行耗時操作1");
NSLog(@"執(zhí)行耗時操作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í)行耗時操作2
NSLog(@"執(zhí)行耗時操作2------%@",[NSThread currentThread]);
NSLog(@"執(zhí)行耗時操作2");
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//執(zhí)行耗時操作3
NSLog(@"執(zhí)行耗時操作3------%@",[NSThread currentThread]);
NSLog(@"執(zhí)行耗時操作3");
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"前面的異步操作已完成");
});
//dispatch_group_create()可以創(chuàng)建一個完全的線程控制,這這個group中的線程,無論該線程是否新開異步線程,
//dispatch_group_notify都會在該group線程所有內(nèi)容執(zhí)行完成以后,再執(zhí)行相關(guān)內(nèi)容(但是sleep(1); 模擬異步請求,dispatch_group_notify不會等待 如果要精確確定異步的執(zhí)行完畢,可以用dispatch_group_enter, dispatch_group_leave來管理)
//所謂異步執(zhí)行就是將當前在異步執(zhí)行的代碼以函數(shù)塊形式排隊放到線程(系統(tǒng)分配的線程,不一定是目前執(zhí)行的線程)執(zhí)行的最后
//由于執(zhí)行的線程不一致,所以完成先后順序也不一致
//對于內(nèi)部再次異步的的內(nèi)容(如網(wǎng)絡請求),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 指定重復次數(shù) 指定6次
* @param queue 追加對象的Dispatch Queue
* @param index 帶有參數(shù)的Block, index的作用是為了按執(zhí)行的順序區(qū)分各個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.柵欄操作時候,只能攔截該線程中第一層異步操作的內(nèi)容,對第一層中再次異步操作的線程無法攔截,同時屬于這個線程的也不行
*/
// 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");
});
//延時執(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"); // 任務1
dispatch_async(queue, ^{
NSLog(@"2"); // 任務2
dispatch_sync(queue, ^{
NSLog(@"3"); // 任務3
});
NSLog(@"4"); // 任務4
});
NSLog(@"5"); // 任務5
/**
執(zhí)行任務1;
遇到異步線程,將【任務2、同步線程、任務4】加入串行隊列中。因為是異步線程,所以在主線程中的任務5不必等待異步線程中的所有任務完成;
因為任務5不必等待,所以2和5的輸出順序不能確定;
任務2執(zhí)行完以后,遇到同步線程,這時,將任務3加入串行隊列;
又因為任務4比任務3早加入串行隊列,所以,任務3要等待任務4完成以后,才能執(zhí)行。但是任務3所在的同步線程會阻塞,所以任務4必須等任務3執(zhí)行完以后再執(zhí)行。這就又陷入了無限的等待中,造成死鎖。
*/
}
semaphore信號量的應用
信號量使用的主要三個函數(shù)
//創(chuàng)建信號量,參數(shù):信號量的初值,如果小于0則會返回NULL
dispatch_semaphore_create(信號量值)
//等待降低信號量
dispatch_semaphore_wait(信號量,等待時間)
//提高信號量
dispatch_semaphore_signal(信號量)
其實,這有點類似鎖機制了,只不過信號量都是系統(tǒng)幫助我們處理了,我們只需要在執(zhí)行線程之前,設(shè)定一個信號量值(這個值必須是大于或者等于0),關(guān)于這個值的理解我們可以從下面兩個例子中獲取比較準確的定義;
一、模擬多線程操作時幾個任務同時進行,所有結(jié)果完成后才進行處理
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(@"=================模擬多線程操作時幾個任務同時進行,完成后才輸出結(jié)果======================");
});
二、用于多線程,線程數(shù)量的控制
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t queue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//任務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);
});
//任務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);
});
//任務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信號量測試======================
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
=================模擬多線程操作時幾個任務同時進行,完成后才輸出結(jié)果======================