一、基礎概念
1、GCD簡介
Grand Central Dispatch 簡稱(GCD)是蘋果公司開發的技術。以優化應用程序支持多核心處理器和其他的對稱多處理系統的系統。
•GCD屬於函數級的多線程,性能更高,功能也更加強大。
•它首次發佈在Mac OS X 10.6 ,iOS 4及以上也可用。
2、GCD核心概念
任務:具有一定功能的代碼段。一般是一個block或者函數。
•分發隊列:GCD以隊列的方式進行工作,例如FIFO。
•GCD會根據分發隊列的類型,創建合適數量的線程執行隊列中的任務。
3、GCD中兩種隊列
dispatch queue分爲下面2種:
•SerialQueue併發隊列:一次只執行一個任務。Serial queue通常用於同步訪問特定的資源或數據。當你創建多個Serial queue時,雖然它們各自是同步執行的,但Serial queue與Serial queue之間是併發執行的。SerialQueue能實現線程同步
•Concurrent串行隊列:可以併發地執行多個任務,但是遵守FIFO
4、GCD的功能:
dispatch_async() 往隊列中添加任務,任務會排隊執行
dispatch_after() 往隊列中添加任務,任務不但會排隊,還會在延遲的時間點執行
dispatch_apply() 往隊列中添加任務,任務會重複執行n次
dispatch_group_async() 將任務添加到隊列中,並添加分組標記
dispatch_group_notify() 將任務添加到隊列中,當某個分組的所有任務執行完之後,此任務纔會執行
dispatch_barrier_async() 將任務添加到隊列中,此任務執行的時候,其他任務停止執行
dispatch_once() 任務添加到隊列中,但任務在程序運行過程中,只執行一次
dispatch_sync() 將任務添加到隊列中,block不執行完,下面代碼不會執行
dispatch_async_f() 將任務添加到隊列中,任務是函數非block
一些使用案例:
1、延遲執行:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC));
dispatch_after(time , dispatch_get_main_queue(), ^{
NSLog(@"延遲5s執行");
});
NSLog(@"是否阻塞主線程"); // 不會
2、只執行一次
static dispatch_once_t oncetoken;
dispatch_once(&oncetoken, ^{
NSLog(@"只執行一次");
});
可以用來實現單例,很多面試會考,最好能手寫出來:
+ (Class *)shareInstance {
static Class *instance = nil;
static dispatch_once_t oncetoken;
dispatch_once(&oncetoken, ^{
instance = [[Class alloc] init];
});
return instance;
}
3、在子線程中,返回主線程
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主線程");
});
一、獲取併發隊列:
方法1:直接使用默認提供的`全局併發隊列`
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // (擴展參數:暫時用不上)
// DISPATCH_QUEUE_PRIORITY_HIGH 2 高
// DISPATCH_QUEUE_PRIORITY_DEFAULT 0 默認
// DISPATCH_QUEUE_PRIORITY_LOW (-2) 低
// DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN 後臺
方法2:自己創建
dispatch_queue_t queue = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_CONCURRENT);
二、獲取串行隊列:
方法1:直接使用主線程
dispatch_queue_t mainQueue = dispatch_get_main_queue();
方法2:自己創建
dispatch_queue_t queue1 = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_SERIAL);
下面來測試混合搭配使用:
1、異步-全局併發:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"執行1:%@", [NSThread currentThread]);
sleep(2);
NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"執行2:%@", [NSThread currentThread]);
sleep(3);
NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"執行3:%@", [NSThread currentThread]);
sleep(1);
NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果:
// 是否阻塞主線程
// 執行1:<NSThread: 0x600000a69280>{number = 5, name = (null)}
// 執行3:<NSThread: 0x600000a54e80>{number = 6, name = (null)}
// 執行2:<NSThread: 0x600000a58780>{number = 7, name = (null)}
// 完成3:<NSThread: 0x600000a54e80>{number = 6, name = (null)}
// 完成1:<NSThread: 0x600000a69280>{number = 5, name = (null)}
// 完成2:<NSThread: 0x600000a58780>{number = 7, name = (null)}
結論1 異步-全局併發:開啓多個線程,併發執行,不阻塞
2、異步-併發
dispatch_queue_t queue = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"執行1:%@", [NSThread currentThread]);
sleep(2);
NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"執行2:%@", [NSThread currentThread]);
sleep(3);
NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"執行3:%@", [NSThread currentThread]);
sleep(1);
NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果:
// 執行1:<NSThread: 0x600002116400>{number = 5, name = (null)}
// 執行2:<NSThread: 0x60000211df80>{number = 6, name = (null)}
// 執行3:<NSThread: 0x600002116440>{number = 7, name = (null)}
// 是否阻塞主線程
// 完成3:<NSThread: 0x600002116440>{number = 7, name = (null)}
// 完成1:<NSThread: 0x600002116400>{number = 5, name = (null)}
// 完成2:<NSThread: 0x60000211df80>{number = 6, name = (null)}
結論2 異步-併發:開啓多個線程,併發執行,不阻塞
3、異步-主串行
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"執行1:%@", [NSThread currentThread]);
sleep(2);
NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"執行2:%@", [NSThread currentThread]);
sleep(3);
NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"執行3:%@", [NSThread currentThread]);
sleep(1);
NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果:
// 是否阻塞主線程
// 執行1:<NSThread: 0x60000088cdc0>{number = 1, name = main}
// 完成1:<NSThread: 0x60000088cdc0>{number = 1, name = main}
// 執行2:<NSThread: 0x60000088cdc0>{number = 1, name = main}
// 完成2:<NSThread: 0x60000088cdc0>{number = 1, name = main}
// 執行3:<NSThread: 0x60000088cdc0>{number = 1, name = main}
// 完成3:<NSThread: 0x60000088cdc0>{number = 1, name = main}
結論3 異步-主串行:主線程中,順序執行,不阻塞
4、異步-串行
dispatch_queue_t queue = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"執行1:%@", [NSThread currentThread]);
sleep(2);
NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"執行2:%@", [NSThread currentThread]);
sleep(3);
NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"執行3:%@", [NSThread currentThread]);
sleep(1);
NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果:
// 是否阻塞主線程
// 執行1:<NSThread: 0x600003e45200>{number = 3, name = (null)}
// 完成1:<NSThread: 0x600003e45200>{number = 3, name = (null)}
// 執行2:<NSThread: 0x600003e45200>{number = 3, name = (null)}
// 完成2:<NSThread: 0x600003e45200>{number = 3, name = (null)}
// 執行3:<NSThread: 0x600003e45200>{number = 3, name = (null)}
// 完成3:<NSThread: 0x600003e45200>{number = 3, name = (null)}
結論4 異步-串行:開啓一個線程,順序執行,不阻塞
5、同步-全局併發
// (如果用async加入,不會跟sync的在一個線程裏)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(queue, ^{
NSLog(@"執行1:%@", [NSThread currentThread]);
sleep(2);
NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"執行2:%@", [NSThread currentThread]);
sleep(3);
NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"執行3:%@", [NSThread currentThread]);
sleep(1);
NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 不會
// 執行結果:
// 執行1:<NSThread: 0x600000675880>{number = 1, name = main}
// 完成1:<NSThread: 0x600000675880>{number = 1, name = main}
// 執行2:<NSThread: 0x600000675880>{number = 1, name = main}
// 完成2:<NSThread: 0x600000675880>{number = 1, name = main}
// 執行3:<NSThread: 0x600000675880>{number = 1, name = main}
// 完成3:<NSThread: 0x600000675880>{number = 1, name = main}
// 是否阻塞主線程
結論5 同步-全局併發:在主線程中,順序執行,阻塞
6、同步-併發
dispatch_queue_t queue = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
NSLog(@"執行1:%@", [NSThread currentThread]);
sleep(2);
NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"執行2:%@", [NSThread currentThread]);
sleep(3);
NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"執行3:%@", [NSThread currentThread]);
sleep(1);
NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 會 (因爲:沒有開啓新線程)
// 執行結果:
// 執行1:<NSThread: 0x600000ad9580>{number = 1, name = main}
// 完成1:<NSThread: 0x600000ad9580>{number = 1, name = main}
// 執行2:<NSThread: 0x600000ad9580>{number = 1, name = main}
// 完成2:<NSThread: 0x600000ad9580>{number = 1, name = main}
// 執行3:<NSThread: 0x600000ad9580>{number = 1, name = main}
// 完成3:<NSThread: 0x600000ad9580>{number = 1, name = main}
// 是否阻塞主線程
結論6 同步-併發:在主線程中,順序執行,阻塞
7、同步-主串行
// 例:之前在百度面試遇到的題
NSLog(@"1");
dispatch_sync(dispatch_get_main_queue(), ^{ // 串行 同步隊列
NSLog(@"2");
});
NSLog(@"3");
// 輸出:1
// 3加入隊列 2加入隊列;FIFO:3等待2執行 而2在3的後面
// 所以造成死鎖(crash: Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0))
結論7 同步-主串行:死鎖,阻塞
8、同步-串行
dispatch_queue_t queue = dispatch_queue_create("moxiaoyan", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"執行1:%@", [NSThread currentThread]);
sleep(2);
NSLog(@"完成1:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"執行2:%@", [NSThread currentThread]);
sleep(3);
NSLog(@"完成2:%@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"執行3:%@", [NSThread currentThread]);
sleep(1);
NSLog(@"完成3:%@", [NSThread currentThread]);
});
NSLog(@"是否阻塞主線程"); // 會
// 執行結果:
// 執行1:<NSThread: 0x600003371880>{number = 1, name = main}
// 完成1:<NSThread: 0x600003371880>{number = 1, name = main}
// 執行2:<NSThread: 0x600003371880>{number = 1, name = main}
// 完成2:<NSThread: 0x600003371880>{number = 1, name = main}
// 執行3:<NSThread: 0x600003371880>{number = 1, name = main}
// 完成3:<NSThread: 0x600003371880>{number = 1, name = main}
// 是否阻塞主線程
結論8 同步-串行:主線程中,順序執行,阻塞
總結:
同步:同步函數不具備開啓線程的能力,無論是什麼隊列都不會開啓線程;
異步:異步函數具備開啓線程的能力,開啓幾條線程由隊列決定(串行隊列只會開啓一條新的線程,併發隊列會開啓多條線程)
異步 async | 同步 sync | |
---|---|---|
全局併發 global_queue | 開啓多個線程,併發執行,不阻塞 | 主線程中,順序執行,阻塞 |
自創併發 SERIAL | 開啓多個線程,併發執行,不阻塞 | 主線程中,順序執行,阻塞 |
主串行 main_queue | 主線程中,順序執行,不阻塞 | 死鎖 !!!,阻塞 |
自創串行 CONCURRENT | 開啓一個線程,順序執行,不阻塞 | 主線程中,順序執行,阻塞 |
同步 和 異步 添加隻影響是不是阻塞當前線程,和任務的串行或並行執行沒有關係