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