Grand Central Dispatch (GCD)
是Apple開發的一個多核編程的解決方法。dispatch queue
分成以下三種:
- 運行在主線程的
Main_queue
,通過dispatch_get_main_queue
獲取。dispatch_get_main_queue
也是一種dispatch_queue_t
。 - 并行隊列
global dispatch queue
,通過dispatch_get_global_queue
獲取,由系統創建三個不同優先級的dispatch_queue
。并行隊列的執行順序與其加入隊列的順序相同。 - 串行隊列
serial queues
一般用于按順序同步訪問,可創建任意數量的串行隊列,各個串行隊列之間是并發的。
當想要任務按照某一個特定的順序執行時,串行隊列是很有用的。串行隊列在同一個時間只執行一個任務。我們可以使用串行隊列代替鎖去保護共享的數據。和鎖不同,一個串行隊列可以保證任務在一個可預知的順序下執行。
serial queues通過dispatch_queue_create
創建,可以使用函數dispatch_retain
和dispatch_release
去增加或者減少引用計數。
嚴格來說,GCD并非多線程,而是一個隊列。
GCD的用法:
1、并行隊列 + 同步執行
- (void)gcdDemo1 {
/*
DISPATCH_QUEUE_CONCURRENT 并發
DISPATCH_QUEUE_SERIAL 串行
*/
NSLog(@"嘿嘿-----------");
dispatch_queue_t q = dispatch_queue_create("ptl", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(q, ^{
for (NSInteger i = 0; i < 10; i ++) {
// 任務在并發子線程執行 所以下面的"哈哈" 可能在for之前 也可能之后
dispatch_async(q, ^{
NSLog(@"??(%zd)--%@", i, [NSThread currentThread]);
});
}
});
NSLog(@"哈哈-----------");
}
2、 并行隊列 + 異步執行
- (void)gcdDemo2 {
/*
先執行"嘿嘿", 下面的代碼都是隨機執行 "哈哈"可能在for前,后或者中間打印
*/
NSLog(@"嘿嘿-----------");
dispatch_queue_t q = dispatch_queue_create("ptl", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(q, ^{
for (NSInteger i = 0; i < 10; i ++) {
dispatch_async(q, ^{
NSLog(@"??(%zd)--%@", i, [NSThread currentThread]);
});
}
});
NSLog(@"哈哈-----------");
}
3、串行隊列 + 同步執行
- (void)gcdDemo3 {
/*
DISPATCH_QUEUE_SERIAL : 串行
按照順序執行
*/
NSLog(@"嘿嘿-----------");
dispatch_queue_t q = dispatch_queue_create("ptl", DISPATCH_QUEUE_SERIAL);
dispatch_sync(q, ^{
for (NSInteger i = 0; i < 10; i ++) {
dispatch_async(q, ^{
NSLog(@"??(%zd)--%@", i, [NSThread currentThread]);
});
}
});
NSLog(@"哈哈-----------");
}
4、串行隊列 + 異步執行
- (void)gcdDemo4 {
/*
先執行"嘿嘿", 后面的會隨機執行, "哈哈"可能會插入到for中打印
for是在串行異步執行 所以子線程會執行完當前任務后才會執行下次任務
*/
NSLog(@"嘿嘿-----------");
dispatch_queue_t q = dispatch_queue_create("ptl", DISPATCH_QUEUE_SERIAL);
dispatch_async(q, ^{
for (NSInteger i = 0; i < 2; i ++) {
dispatch_async(q, ^{
NSLog(@"??(%zd)--%@", i, [NSThread currentThread]);
});
}
});
NSLog(@"哈哈-----------");
}
5、主隊列 + 同步執行
- (void)gcdDemo5 {
/*
"嘿嘿"等待for的任務執行完往下執行 而for又等待"嘿嘿"執行完往下執行 所以互相等待 程序卡死 崩潰
*/
NSLog(@"嘿嘿-----------");
dispatch_sync(dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"??(%zd)--%@", i, [NSThread currentThread]);
}
});
NSLog(@"哈哈-----------");
}
6、主隊列 + 異步執行
- (void)gcdDemo6 {
/*
先執行"嘿嘿"->"哈哈"->for里面 for里面的任務會在主隊列執行 添加到主隊列后不會立馬就執行的 會等待一會 所以"哈哈"在for之前就執行了 因為它本身就在主線程中無需等待
*/
NSLog(@"嘿嘿-----------");
dispatch_async(dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"??(%zd)--%@", i, [NSThread currentThread]);
}
});
NSLog(@"哈哈-----------");
}
7、柵欄方法
- dispatch_barrier_async
有時需要異步執行兩組操作,而且第一組操作執行完之后,才能開始執行第二組操作。這樣我們就需要一個相當于柵欄一樣的一個方法將兩組異步執行的操作組給分割起來,操作組里可以包含一個或多個任務。這就需要用到dispatch_barrier_async
方法在兩個操作組間形成柵欄。
- (void)gcdDemo7 {
/*
"1"和"2"處于子線程會隨機打印順序 然后打印柵欄"3" 最后打印"4"
*/
dispatch_queue_t q = dispatch_queue_create("ptl", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(q, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(q, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_barrier_async(q, ^{
for (NSInteger i = 0; i < 10; i ++) {
// // 加入到異步后 下面的"4"可能在for之前打印, 可能在中間打印, 也可能在for后面打印
// dispatch_async(q, ^{
// NSLog(@"----3 barrier-----%@", [NSThread currentThread]);
// });
NSLog(@"----3 barrier-----%@", [NSThread currentThread]);
}
});
dispatch_async(q, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
}
8、快速迭代方法 dispatch_apply
dispatch_apply
函數是dispatch_sync
函數和Dispatch_Group
的關聯API,該函數按指定的次數將指定的Block追加到指定的Dispatch_Queue
中,并等到全部的處理執行結束。
- (void)gcdDemo8 {
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
/**
同時遍歷6次 不按順序輸出
@param iterations 遍歷的次數
@param queue 在哪個隊列
@param size_t index下標號
*/
dispatch_apply(6, q, ^(size_t index) {
NSLog(@"%zd----%@", index, [NSThread currentThread]);
});
}
9、隊列組 dispatch_group
多個任務處理完畢之后想執行結束處理,這種需求會經常出現。如果只是使用一個Serial Dispatch Queue(串行隊列)時,只要將想執行的處理全部追加到該串行隊列中并在最后追加結束處理即可,但是在使用Concurrent Queue 時,可能會同時使用多個Dispatch Queue時,代碼就會變得很復雜。在這種情況下,就可以使用Dispatch Group。
- (void)gcdDemo9 {
/*
"3"會等待前面的"1""2"打印完才會打印, "1""2"異步操作會交替打印
*/
// 創建一個組隊列
dispatch_group_t group = dispatch_group_create();
// 創建一個異步組隊列
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執行1個耗時的異步操作
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"1----%zd---%@",i, [NSThread currentThread]);
}
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執行1個耗時的異步操作
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"2----%zd---%@",i, [NSThread currentThread]);
}
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執行完畢后,回到主線程...
for (NSInteger i = 0; i < 10; i ++) {
NSLog(@"3----%zd---%@",i, [NSThread currentThread]);
}
});
}
10、信號量dispatch_semaphore
項目中的業務接口請求的時候需要Token驗證。我們最簡化這個需求就是:兩個請求,請求1成功返回所需參數之后,才能開始請求2。
// 信號量
- (void)dispatch_semaphore {
/*
//創建信號量
dispatch_semaphore_create
//發送信號量
dispatch_semaphore_signal
//等待信號量
dispatch_semaphore_wait
*/
dispatch_async(dispatch_queue_create("ptl", DISPATCH_QUEUE_CONCURRENT), ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// 請求接口1
[self getToken:semaphore];
//此時的信號量為0,只有token請求成功發送信號量之后,才會往下執行[self request]方法,否則會一直等下去;
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 請求接口2
[self requestData1:semaphore];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 請求接口3
[self requestData2];
});
}
- (void)getToken:(dispatch_semaphore_t)semaphore
{
// 模仿網絡請求
for (NSInteger i = 0; i < 20; i ++) {
NSLog(@"請求1-------%zd", i);
// 模仿請求成功
//成功拿到token,發送信號量
dispatch_semaphore_signal(semaphore);
}
NSLog(@"------------請求1成功-----------------");
}
- (void)requestData1:(dispatch_semaphore_t)semaphore {
for (NSInteger i = 0; i < 20; i ++) {
NSLog(@"請求2-------%zd", i);
dispatch_semaphore_signal(semaphore);
}
NSLog(@"------------請求2成功-----------------");
}
- (void)requestData2 {
for (NSInteger i = 0; i < 20; i ++) {
NSLog(@"請求3-------%zd", i);
}
NSLog(@"------------請求3成功-----------------");
}
Github: https://github.com/soliloquy-local/GCD.git
以上就是對GCD各個情況的簡單介紹,主要以代碼為主...??