本文主要知識點來自:
http://www.reibang.com/p/2d57c72016c6
作者:[行走少年郎]
GCD通過我們的同步異步和串行并發(fā),來決定是否創(chuàng)建新的線程还蹲,(是否實現(xiàn)多線程)。一個線程可以有多個隊列,并且主隊列在主線程所以才特殊嫉戚。尤其要理解新隊列并不一定會產(chǎn)生新的線程锦庸。
首先記錄一下GCD的重要知識點:
任務:同步執(zhí)行(sync)瓣俯,異步執(zhí)行(async)
隊列:串行隊列,并發(fā)隊列鹅颊,主隊列(串行)
創(chuàng)建隊列:
// 串行隊列的創(chuàng)建方法
dispatch_queue_t queue = dispatch_queue_create("name", DISPATCH_QUEUE_SERIAL);
// 并發(fā)隊列的創(chuàng)建方法
dispatch_queue_t queue = dispatch_queue_create("name", DISPATCH_QUEUE_CONCURRENT);
//主隊列獲取方法:
dispatch_queue_t queue = dispatch_get_main_queue();
// 全局并發(fā)隊列的獲取方法
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
任務的創(chuàng)建方法(應該說是將block添加進隊列方法):
// 同步執(zhí)行任務創(chuàng)建方法
dispatch_sync(queue, ^{
});
// 異步執(zhí)行任務創(chuàng)建方法
dispatch_async(queue, ^{
});
注意同步添加任務到當前線程會造成死鎖,(常見的就是主線程中sync+主隊列:比如我們在a方法里sync添加任務b到主隊列墓造,a方法是主線程執(zhí)行的堪伍,即主線程執(zhí)行a的過程中需要做添加b的操作,但是添加b的操作需要等a執(zhí)行完畢才可以添加觅闽。)
舉個通俗的例子:
假設你(隊列)面前一排便簽(任務)帝雇,你只能看完一個便簽,并且做完上上面寫的東西后才能看后面的便簽(串行執(zhí)行)蛉拙,
這時候第三個便簽上貼了個小標簽(sync添加任務)尸闸。
形成死鎖,小標簽等你做完第三個便簽的事情才能看孕锄,但是不看小標簽就不算做完第三個便簽的事吮廉。
解決辦法:
1用async:將小標簽上的東西寫到新的便簽上,在你執(zhí)行完其他便簽的間隙添加到便簽里面
2使用sync調(diào)用其他隊列:就是第三個便簽上寫著:將下面的小標簽貼到別人的便簽后面畸肆。這時候你不需要看小標簽內(nèi)容也可以完成這個任務宦芦。
GCD常用方法:
1.柵欄方法:dispatch_barrier_async
在執(zhí)行完柵欄前面的操作之后,才執(zhí)行柵欄操作轴脐,最后再執(zhí)行柵欄后邊的操作调卑。
/**
* 柵欄方法 dispatch_barrier_async
*/
- (void)barrier {
dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
// 追加任務 1
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當前線程
});
dispatch_async(queue, ^{
// 追加任務 2
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當前線程
});
dispatch_barrier_async(queue, ^{
// 追加任務 barrier
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"barrier---%@",[NSThread currentThread]);// 打印當前線程
});
dispatch_async(queue, ^{
// 追加任務 3
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當前線程
});
dispatch_async(queue, ^{
// 追加任務 4
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"4---%@",[NSThread currentThread]); // 打印當前線程
});
}
2.延時執(zhí)行方法:dispatch_after
并不是在指定時間之后才開始執(zhí)行處理,而是在指定時間之后將任務追加到主隊列中大咱。
- (void)after {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程
NSLog(@"asyncMain---begin");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2.0 秒后異步追加任務代碼到主隊列令野,并開始執(zhí)行
NSLog(@"after---%@",[NSThread currentThread]); // 打印當前線程
});
}
3. 一次性代碼(只執(zhí)行一次):dispatch_once
即使在多線程的環(huán)境下,dispatch_once 也可以保證線程安全徽级。
- (void)once {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執(zhí)行 1 次的代碼(這里面默認是線程安全的)
});
}
4.快速迭代方法:dispatch_apply
按照指定的次數(shù)將指定的任務追加到指定的隊列中气破,并等待全部隊列執(zhí)行結束。
如果是在串行隊列中使用 dispatch_apply餐抢,那么就和 for 循環(huán)一樣现使,按順序同步執(zhí)行低匙。但是這樣就體現(xiàn)不出快速迭代的意義了。
我們可以利用并發(fā)隊列進行異步執(zhí)行碳锈。比如說遍歷 0~5 這 6 個數(shù)字顽冶,for 循環(huán)的做法是每次取出一個元素,逐個遍歷售碳。dispatch_apply 可以 在多個線程中同時(異步)遍歷多個數(shù)字强重。
還有一點,無論是在串行隊列贸人,還是并發(fā)隊列中间景,dispatch_apply 都會等待全部任務執(zhí)行完畢,這點就像是同步操作艺智,也像是隊列組中的 dispatch_group_wait方法倘要。
- (void)apply {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"apply---begin");
dispatch_apply(6, queue, ^(size_t index) {
NSLog(@"%zd---%@",index, [NSThread currentThread]);
});
NSLog(@"apply---end");
}
5.隊列組:dispatch_group
dispatch_group_notify
- (void)groupNotify {
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務 1
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務 2
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步任務 1、任務 2 都執(zhí)行完畢后十拣,回到主線程執(zhí)行下邊任務
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
});
}
dispatch_group_wait(阻塞當前線程)
/**
* 隊列組 dispatch_group_wait
*/
- (void)groupWait {
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務 1
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務 2
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
});
// 等待上面的任務全部完成后封拧,會往下繼續(xù)執(zhí)行(會阻塞當前線程)
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"group---end");
}
dispatch_group_enter、dispatch_group_leave
/**
* 隊列組 dispatch_group_enter夭问、dispatch_group_leave
*/
- (void)groupEnterAndLeave {
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
// 追加任務 1
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當前線程
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
// 追加任務 2
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當前線程
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后泽西,回到主線程.
[NSThread sleepForTimeInterval:2]; // 模擬耗時操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當前線程
NSLog(@"group---end");
});
}
6 dispatch_semaphore(信號量)
dispatch_semaphore_create:創(chuàng)建一個 Semaphore 并初始化信號的總量
dispatch_semaphore_signal:發(fā)送一個信號,讓信號總量加 1
dispatch_semaphore_wait:可以使總信號量減 1缰趋,信號總量小于 0 時就會一直等待(阻塞所在線程)尝苇,否則就可以正常執(zhí)行。