同步、異步免绿、串行唧席、并行的概念
同步/異步:指的是能否開啟新的線程,同步不能開啟新的線程嘲驾,異步可以淌哟。
串行/并行:指的是任務(wù)的執(zhí)行方式,串行是指有多個(gè)任務(wù)時(shí)距淫,各個(gè)任務(wù)按順序執(zhí)行绞绒,完成一個(gè)之后才能進(jìn)行下一個(gè)。并行指的是多個(gè)任務(wù)可以同時(shí)執(zhí)行榕暇。
異步是多個(gè)任務(wù)并行的前提條件。
名稱 | 特點(diǎn) |
---|---|
同步執(zhí)行 | 不具備開啟新線程的能力, 任務(wù)創(chuàng)建后要執(zhí)行完才能繼續(xù)往下走 |
異步執(zhí)行 | 具備開啟新線程的能力彤枢, 任務(wù)創(chuàng)建后可以先繞過狰晚,然后再執(zhí)行 |
串行隊(duì)列 | 隊(duì)列中的任務(wù)要按順序執(zhí)行 |
并行隊(duì)列 | 隊(duì)列中的任務(wù)同時(shí)執(zhí)行 |
線程、任務(wù)缴啡、隊(duì)列的概念
名稱 | 特點(diǎn) |
---|---|
線程 | 程序執(zhí)行任務(wù)的最小調(diào)度單位 |
任務(wù) | 說白了就是一段代碼壁晒,在GCD中, 任務(wù)就是Block中要執(zhí)行的內(nèi)容 |
隊(duì)列 | 用來存放“任務(wù)”的一個(gè)數(shù)組 |
所有組合
并行隊(duì)列 | 串行隊(duì)列 | 主隊(duì)列 | |
---|---|---|---|
異步執(zhí)行 | 開啟多個(gè)新線程业栅,任務(wù)同時(shí)執(zhí)行 | 開啟一個(gè)新線程秒咐,任務(wù)按順序執(zhí)行 | 不開啟新的線程,任務(wù)按順序執(zhí)行 |
同步執(zhí)行 | 不開啟新線程碘裕,任務(wù)按順序執(zhí)行 | 不開啟新線程携取,任務(wù)按順序執(zhí)行 | 死鎖 |
死鎖:兩個(gè)(多個(gè))線程都要等待對(duì)方完成某個(gè)操作才能進(jìn)行下一步,這時(shí)就會(huì)發(fā)生死鎖帮孔。
代碼編程實(shí)現(xiàn)
獲取隊(duì)列(三種方式)
1雷滋、自定義隊(duì)列
//自定義并行隊(duì)列
-(dispatch_queue_t)createConcurrentQueue{
dispatch_queue_t queue = dispatch_queue_create("LN_Concurrent", DISPATCH_QUEUE_CONCURRENT);
return queue;
}
//自定義串行隊(duì)列
-(dispatch_queue_t)createSerialQueue{
dispatch_queue_t queue = dispatch_queue_create("LN_Serial", DISPATCH_QUEUE_SERIAL);
return queue;
}
2、主線程串行隊(duì)列
//獲取主線程串行隊(duì)列
-(dispatch_queue_t)getMainSerialQueue{
dispatch_queue_t queue = dispatch_get_main_queue();
return queue;
}
3文兢、全局并發(fā)隊(duì)列
//獲取全局并發(fā)隊(duì)列
-(dispatch_queue_t)getGlobalConcurrentQueue{
/*
* 第一個(gè)參數(shù):優(yōu)先級(jí)別
DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_GACKGROUND
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
return queue;
}
為隊(duì)列添加任務(wù)(兩種方式)
1晤斩、異步添加任務(wù)
//異步
-(void)addTaskWithAsyncInQueue:(dispatch_queue_t)queue{
dispatch_async(queue, ^{
NSLog(@"任務(wù)1開始");
sleep(5);
NSLog(@"任務(wù)1結(jié)束");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2開始");
sleep(2);
NSLog(@"任務(wù)2結(jié)束");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)3開始");
sleep(1);
NSLog(@"任務(wù)3結(jié)束");
});
}
2、同步添加任務(wù)
-(void)addTaskWithSyncInQueue:(dispatch_queue_t)queue{
dispatch_sync(queue, ^{
NSLog(@"任務(wù)1開始");
sleep(5);
NSLog(@"任務(wù)1結(jié)束");
});
dispatch_sync(queue, ^{
NSLog(@"任務(wù)2開始");
sleep(2);
NSLog(@"任務(wù)2結(jié)束");
});
dispatch_sync(queue, ^{
NSLog(@"任務(wù)3開始");
sleep(1);
NSLog(@"任務(wù)3結(jié)束");
});
}
組合執(zhí)行
(一)異步+并行
//異步+并行
-(void)lnAsyncConcurrent{
dispatch_queue_t queue = [self createConcurrentQueue];
NSLog(@"======start=====");
[self addTaskWithAsyncInQueue:queue];
NSLog(@"======end=====");
}
執(zhí)行輸出結(jié)果:
2018-04-17 14:28:03.797234+0800 ThreadProject[1708:124655] ======start=====
2018-04-17 14:28:03.797451+0800 ThreadProject[1708:124655] ======end=====
2018-04-17 14:28:03.797510+0800 ThreadProject[1708:124714] 任務(wù)1開始
2018-04-17 14:28:03.797512+0800 ThreadProject[1708:124711] 任務(wù)3開始
2018-04-17 14:28:03.797512+0800 ThreadProject[1708:124713] 任務(wù)2開始
2018-04-17 14:28:04.802118+0800 ThreadProject[1708:124711] 任務(wù)3結(jié)束
2018-04-17 14:28:05.799360+0800 ThreadProject[1708:124713] 任務(wù)2結(jié)束
2018-04-17 14:28:08.801884+0800 ThreadProject[1708:124714] 任務(wù)1結(jié)束
在代碼的任務(wù)3中設(shè)置斷點(diǎn)姆坚,查看線程數(shù)
總結(jié):
- 開了三個(gè)新線程
- 函數(shù)在執(zhí)行時(shí)澳泵,先打印了start和end,再回頭執(zhí)行這三個(gè)任務(wù)
這是異步執(zhí)行的結(jié)果兼呵,異步執(zhí)行會(huì)開啟新線程烹俗,任務(wù)可以先繞過不執(zhí)行,回頭再來執(zhí)行萍程。
- 三個(gè)任務(wù)同時(shí)開始
這是并發(fā)的結(jié)果
(二)異步+串行
//異步+串行
-(void)lnAsyncSerial{
dispatch_queue_t queue = [self createSerialQueue];
NSLog(@"======start=====");
[self addTaskWithAsyncInQueue:queue];
NSLog(@"======end=====");
}
執(zhí)行輸出結(jié)果:
2018-04-17 15:35:17.971527+0800 ThreadProject[2071:164583] ======start=====
2018-04-17 15:35:17.971778+0800 ThreadProject[2071:164583] ======end=====
2018-04-17 15:35:17.971823+0800 ThreadProject[2071:164636] 任務(wù)1開始
2018-04-17 15:35:22.974270+0800 ThreadProject[2071:164636] 任務(wù)1結(jié)束
2018-04-17 15:35:22.974649+0800 ThreadProject[2071:164636] 任務(wù)2開始
2018-04-17 15:35:24.978868+0800 ThreadProject[2071:164636] 任務(wù)2結(jié)束
2018-04-17 15:35:24.979185+0800 ThreadProject[2071:164636] 任務(wù)3開始
2018-04-17 15:35:25.983574+0800 ThreadProject[2071:164636] 任務(wù)3結(jié)束
總結(jié):相比異步+并行幢妄,這個(gè)的任務(wù)執(zhí)行順序是一個(gè)一個(gè)來的,上一個(gè)任務(wù)結(jié)束了才開始下一個(gè)
任務(wù)茫负。
這是串行的結(jié)果
(三)異步+主隊(duì)列
//異步+主隊(duì)列
-(void)lnAsyncMain{
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"======start=====");
[self addTaskWithAsyncInQueue:queue];
NSLog(@"======end=====");
}
執(zhí)行輸出結(jié)果:
2018-04-17 15:41:25.099769+0800 ThreadProject[2071:164583] ======start=====
2018-04-17 15:41:25.099955+0800 ThreadProject[2071:164583] ======end=====
2018-04-17 15:41:25.101869+0800 ThreadProject[2071:164583] 任務(wù)1開始
2018-04-17 15:41:30.103308+0800 ThreadProject[2071:164583] 任務(wù)1結(jié)束
2018-04-17 15:41:30.103591+0800 ThreadProject[2071:164583] 任務(wù)2開始
2018-04-17 15:41:32.104805+0800 ThreadProject[2071:164583] 任務(wù)2結(jié)束
2018-04-17 15:41:32.105079+0800 ThreadProject[2071:164583] 任務(wù)3開始
2018-04-17 15:42:34.792503+0800 ThreadProject[2071:164583] 任務(wù)3結(jié)束
總結(jié):執(zhí)行輸出結(jié)果與異步+串行是一樣的蕉鸳,這是因?yàn)橹麝?duì)列就是一個(gè)串行隊(duì)列。
不同的是:不開啟新的線程忍法,而是在主線程上運(yùn)行潮尝。
(四)同步+并行
//同步+并行
-(void)lnSyncConcurrent{
dispatch_queue_t queue = [self createConcurrentQueue];
NSLog(@"======start=====");
[self addTaskWithSyncInQueue:queue];
NSLog(@"======end=====");
}
執(zhí)行輸出結(jié)果:
2018-04-17 15:47:48.893351+0800 ThreadProject[2071:164583] ======start=====
2018-04-17 15:47:48.893553+0800 ThreadProject[2071:164583] 任務(wù)1開始
2018-04-17 15:47:53.894956+0800 ThreadProject[2071:164583] 任務(wù)1結(jié)束
2018-04-17 15:47:53.895313+0800 ThreadProject[2071:164583] 任務(wù)2開始
2018-04-17 15:47:55.896732+0800 ThreadProject[2071:164583] 任務(wù)2結(jié)束
2018-04-17 15:47:55.897079+0800 ThreadProject[2071:164583] 任務(wù)3開始
2018-04-17 15:47:56.898450+0800 ThreadProject[2071:164583] 任務(wù)3結(jié)束
2018-04-17 15:47:56.898782+0800 ThreadProject[2071:164583] ======end=====
總結(jié):根據(jù)程序代碼從上往下走,不開啟新線程饿序。
(五)同步+串行
輸出結(jié)果與同步+并行是相同的勉失。
總結(jié):
同步+并行與同步+串行的區(qū)別:同步+并行使用嵌套調(diào)用不會(huì)產(chǎn)生死鎖,同步+串行嵌套調(diào)用會(huì)產(chǎn)生死鎖原探。
(六)同步+主隊(duì)列
//同步+主隊(duì)列
-(void)lnSyncMain{
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"======start=====");
[self addTaskWithSyncInQueue:queue];
NSLog(@"======end=====");
}
死鎖
死鎖產(chǎn)生原因:主隊(duì)列上先有了一個(gè)lnSyncMain這個(gè)任務(wù)乱凿,在lnSyncMain方法中又在主隊(duì)列上添加了任務(wù)顽素。由于是串行,先要lnSyncMain這個(gè)任務(wù)完成徒蟆,才執(zhí)行后添加的任務(wù)胁出。但是lnSyncMain這個(gè)任務(wù)的完成又依賴于添加的block。所以就出現(xiàn)了循環(huán)等待段审,導(dǎo)致死鎖全蝶。
死鎖測試:
//嵌套 同步+并行 (不會(huì)產(chǎn)生死鎖)
-(void)testForLock{
dispatch_queue_t queue = [self createConcurrentQueue];
NSLog(@"======start=====");
dispatch_sync(queue, ^{
NSLog(@"任務(wù)1開始");
dispatch_sync(queue, ^{
NSLog(@"任務(wù)2開始");
NSLog(@"任務(wù)2結(jié)束");
});
NSLog(@"任務(wù)1結(jié)束");
});
NSLog(@"======end=====");
}
//嵌套 同步+串行(會(huì)產(chǎn)生死鎖)
-(void)testForLockTwo{
dispatch_queue_t queue = [self createSerialQueue];
NSLog(@"======start=====");
dispatch_sync(queue, ^{
NSLog(@"任務(wù)1開始");
dispatch_sync(queue, ^{
NSLog(@"任務(wù)2開始");
NSLog(@"任務(wù)2結(jié)束");
});
NSLog(@"任務(wù)1結(jié)束");
});
NSLog(@"======end=====");
}
注意:不要嵌套使用同步執(zhí)行的串行隊(duì)列任務(wù)
GCD其他方法
- dispatch_once
保證在app運(yùn)行期間,block中的代碼只執(zhí)行一次寺枉。常用于單例的初始化抑淫。 - dispatch_barrier_async
1、在并行隊(duì)列中姥闪,等待在dispatch_barrier_async之前加入的任務(wù)全部執(zhí)行完成之后(這些任務(wù)是并發(fā)執(zhí)行的)
2始苇、再執(zhí)行dispatch_barrier_async中的任務(wù)
3、dispatch_barrier_async中的任務(wù)執(zhí)行完成之后甘畅,再去執(zhí)行在dispatch_barrier_async之后加入到隊(duì)列中的任務(wù)(這些任務(wù)是并發(fā)執(zhí)行的)埂蕊。
使用場景:多讀單寫
//異步柵欄(多讀單寫場景)
-(void)lnAsyncBarrier{
dispatch_queue_t queue = [self createConcurrentQueue];
NSLog(@"======start=====");
[self addTaskWithAsyncInQueue:queue];
/*
*1、等待dispatch_barrier_async之前的任務(wù)全部執(zhí)行完
*2疏唾、執(zhí)行dispatch_barrier_async的任務(wù)
*3蓄氧、執(zhí)行dispatch_barrier_async之后的任務(wù)
*/
dispatch_barrier_async(queue, ^{
NSLog(@"柵欄方法");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)5開始");
sleep(3);
NSLog(@"任務(wù)5結(jié)束");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)6開始");
sleep(1);
NSLog(@"任務(wù)6結(jié)束");
});
NSLog(@"======end=====");
}
- dispatch_group_notify
結(jié)合dispatch_group_t一起使用,等待組里的任務(wù)全部完成后槐脏,調(diào)用dispatch_group_notify的block
使用場景:同時(shí)下載多個(gè)圖片喉童,所有圖片下載完成之后去更新UI(回到主線程)
//group queue
-(void)lnGroupQueue{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = [self createConcurrentQueue];
//假設(shè)這個(gè)數(shù)組用于存放圖片的下載地址
NSArray *arrayURLs = @[@"圖片下載地址1",@"圖片下載地址2",@"圖片下載地址3"];
for(NSString *url in arrayURLs){
dispatch_group_async(group, queue, ^{
//根據(jù)url去下載圖片
NSLog(@"%@",url);
});
}
//主線程上操作
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 當(dāng)添加到組中的所有任務(wù)執(zhí)行完成之后會(huì)調(diào)用該Block
NSLog(@"所有圖片已全部下載完成");
});
}