同步、異步、串行、并行的概念
同步/異步:指的是能否開啟新的線程,同步不能開啟新的線程,異步可以。
串行/并行:指的是任務的執(zhí)行方式,串行是指有多個任務時,各個任務按順序執(zhí)行,完成一個之后才能進行下一個。并行指的是多個任務可以同時執(zhí)行。
異步是多個任務并行的前提條件。
名稱 | 特點 |
---|---|
同步執(zhí)行 | 不具備開啟新線程的能力, 任務創(chuàng)建后要執(zhí)行完才能繼續(xù)往下走 |
異步執(zhí)行 | 具備開啟新線程的能力, 任務創(chuàng)建后可以先繞過,然后再執(zhí)行 |
串行隊列 | 隊列中的任務要按順序執(zhí)行 |
并行隊列 | 隊列中的任務同時執(zhí)行 |
線程、任務、隊列的概念
名稱 | 特點 |
---|---|
線程 | 程序執(zhí)行任務的最小調(diào)度單位 |
任務 | 說白了就是一段代碼,在GCD中, 任務就是Block中要執(zhí)行的內(nèi)容 |
隊列 | 用來存放“任務”的一個數(shù)組 |
所有組合
并行隊列 | 串行隊列 | 主隊列 | |
---|---|---|---|
異步執(zhí)行 | 開啟多個新線程,任務同時執(zhí)行 | 開啟一個新線程,任務按順序執(zhí)行 | 不開啟新的線程,任務按順序執(zhí)行 |
同步執(zhí)行 | 不開啟新線程,任務按順序執(zhí)行 | 不開啟新線程,任務按順序執(zhí)行 | 死鎖 |
死鎖:兩個(多個)線程都要等待對方完成某個操作才能進行下一步,這時就會發(fā)生死鎖。
代碼編程實現(xiàn)
獲取隊列(三種方式)
1、自定義隊列
//自定義并行隊列
-(dispatch_queue_t)createConcurrentQueue{
dispatch_queue_t queue = dispatch_queue_create("LN_Concurrent", DISPATCH_QUEUE_CONCURRENT);
return queue;
}
//自定義串行隊列
-(dispatch_queue_t)createSerialQueue{
dispatch_queue_t queue = dispatch_queue_create("LN_Serial", DISPATCH_QUEUE_SERIAL);
return queue;
}
2、主線程串行隊列
//獲取主線程串行隊列
-(dispatch_queue_t)getMainSerialQueue{
dispatch_queue_t queue = dispatch_get_main_queue();
return queue;
}
3、全局并發(fā)隊列
//獲取全局并發(fā)隊列
-(dispatch_queue_t)getGlobalConcurrentQueue{
/*
* 第一個參數(shù):優(yōu)先級別
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;
}
為隊列添加任務(兩種方式)
1、異步添加任務
//異步
-(void)addTaskWithAsyncInQueue:(dispatch_queue_t)queue{
dispatch_async(queue, ^{
NSLog(@"任務1開始");
sleep(5);
NSLog(@"任務1結束");
});
dispatch_async(queue, ^{
NSLog(@"任務2開始");
sleep(2);
NSLog(@"任務2結束");
});
dispatch_async(queue, ^{
NSLog(@"任務3開始");
sleep(1);
NSLog(@"任務3結束");
});
}
2、同步添加任務
-(void)addTaskWithSyncInQueue:(dispatch_queue_t)queue{
dispatch_sync(queue, ^{
NSLog(@"任務1開始");
sleep(5);
NSLog(@"任務1結束");
});
dispatch_sync(queue, ^{
NSLog(@"任務2開始");
sleep(2);
NSLog(@"任務2結束");
});
dispatch_sync(queue, ^{
NSLog(@"任務3開始");
sleep(1);
NSLog(@"任務3結束");
});
}
組合執(zhí)行
(一)異步+并行
//異步+并行
-(void)lnAsyncConcurrent{
dispatch_queue_t queue = [self createConcurrentQueue];
NSLog(@"======start=====");
[self addTaskWithAsyncInQueue:queue];
NSLog(@"======end=====");
}
執(zhí)行輸出結果:
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] 任務1開始
2018-04-17 14:28:03.797512+0800 ThreadProject[1708:124711] 任務3開始
2018-04-17 14:28:03.797512+0800 ThreadProject[1708:124713] 任務2開始
2018-04-17 14:28:04.802118+0800 ThreadProject[1708:124711] 任務3結束
2018-04-17 14:28:05.799360+0800 ThreadProject[1708:124713] 任務2結束
2018-04-17 14:28:08.801884+0800 ThreadProject[1708:124714] 任務1結束
在代碼的任務3中設置斷點,查看線程數(shù)
開啟了三個線程
總結:
- 開了三個新線程
- 函數(shù)在執(zhí)行時,先打印了start和end,再回頭執(zhí)行這三個任務
這是異步執(zhí)行的結果,異步執(zhí)行會開啟新線程,任務可以先繞過不執(zhí)行,回頭再來執(zhí)行。
- 三個任務同時開始
這是并發(fā)的結果
(二)異步+串行
//異步+串行
-(void)lnAsyncSerial{
dispatch_queue_t queue = [self createSerialQueue];
NSLog(@"======start=====");
[self addTaskWithAsyncInQueue:queue];
NSLog(@"======end=====");
}
執(zhí)行輸出結果:
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] 任務1開始
2018-04-17 15:35:22.974270+0800 ThreadProject[2071:164636] 任務1結束
2018-04-17 15:35:22.974649+0800 ThreadProject[2071:164636] 任務2開始
2018-04-17 15:35:24.978868+0800 ThreadProject[2071:164636] 任務2結束
2018-04-17 15:35:24.979185+0800 ThreadProject[2071:164636] 任務3開始
2018-04-17 15:35:25.983574+0800 ThreadProject[2071:164636] 任務3結束
開啟了新線程
總結:相比異步+并行,這個的任務執(zhí)行順序是一個一個來的,上一個任務結束了才開始下一個
任務。
這是串行的結果
(三)異步+主隊列
//異步+主隊列
-(void)lnAsyncMain{
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"======start=====");
[self addTaskWithAsyncInQueue:queue];
NSLog(@"======end=====");
}
執(zhí)行輸出結果:
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] 任務1開始
2018-04-17 15:41:30.103308+0800 ThreadProject[2071:164583] 任務1結束
2018-04-17 15:41:30.103591+0800 ThreadProject[2071:164583] 任務2開始
2018-04-17 15:41:32.104805+0800 ThreadProject[2071:164583] 任務2結束
2018-04-17 15:41:32.105079+0800 ThreadProject[2071:164583] 任務3開始
2018-04-17 15:42:34.792503+0800 ThreadProject[2071:164583] 任務3結束
未開啟新線程
總結:執(zhí)行輸出結果與異步+串行是一樣的,這是因為主隊列就是一個串行隊列。
不同的是:不開啟新的線程,而是在主線程上運行。
(四)同步+并行
//同步+并行
-(void)lnSyncConcurrent{
dispatch_queue_t queue = [self createConcurrentQueue];
NSLog(@"======start=====");
[self addTaskWithSyncInQueue:queue];
NSLog(@"======end=====");
}
執(zhí)行輸出結果:
2018-04-17 15:47:48.893351+0800 ThreadProject[2071:164583] ======start=====
2018-04-17 15:47:48.893553+0800 ThreadProject[2071:164583] 任務1開始
2018-04-17 15:47:53.894956+0800 ThreadProject[2071:164583] 任務1結束
2018-04-17 15:47:53.895313+0800 ThreadProject[2071:164583] 任務2開始
2018-04-17 15:47:55.896732+0800 ThreadProject[2071:164583] 任務2結束
2018-04-17 15:47:55.897079+0800 ThreadProject[2071:164583] 任務3開始
2018-04-17 15:47:56.898450+0800 ThreadProject[2071:164583] 任務3結束
2018-04-17 15:47:56.898782+0800 ThreadProject[2071:164583] ======end=====
總結:根據(jù)程序代碼從上往下走,不開啟新線程。
(五)同步+串行
輸出結果與同步+并行是相同的。
總結:
同步+并行與同步+串行的區(qū)別:同步+并行使用嵌套調(diào)用不會產(chǎn)生死鎖,同步+串行嵌套調(diào)用會產(chǎn)生死鎖。
(六)同步+主隊列
//同步+主隊列
-(void)lnSyncMain{
dispatch_queue_t queue = dispatch_get_main_queue();
NSLog(@"======start=====");
[self addTaskWithSyncInQueue:queue];
NSLog(@"======end=====");
}
死鎖
死鎖產(chǎn)生原因:主隊列上先有了一個lnSyncMain這個任務,在lnSyncMain方法中又在主隊列上添加了任務。由于是串行,先要lnSyncMain這個任務完成,才執(zhí)行后添加的任務。但是lnSyncMain這個任務的完成又依賴于添加的block。所以就出現(xiàn)了循環(huán)等待,導致死鎖。
死鎖測試:
//嵌套 同步+并行 (不會產(chǎn)生死鎖)
-(void)testForLock{
dispatch_queue_t queue = [self createConcurrentQueue];
NSLog(@"======start=====");
dispatch_sync(queue, ^{
NSLog(@"任務1開始");
dispatch_sync(queue, ^{
NSLog(@"任務2開始");
NSLog(@"任務2結束");
});
NSLog(@"任務1結束");
});
NSLog(@"======end=====");
}
//嵌套 同步+串行(會產(chǎn)生死鎖)
-(void)testForLockTwo{
dispatch_queue_t queue = [self createSerialQueue];
NSLog(@"======start=====");
dispatch_sync(queue, ^{
NSLog(@"任務1開始");
dispatch_sync(queue, ^{
NSLog(@"任務2開始");
NSLog(@"任務2結束");
});
NSLog(@"任務1結束");
});
NSLog(@"======end=====");
}
注意:不要嵌套使用同步執(zhí)行的串行隊列任務
GCD其他方法
- dispatch_once
保證在app運行期間,block中的代碼只執(zhí)行一次。常用于單例的初始化。 - dispatch_barrier_async
1、在并行隊列中,等待在dispatch_barrier_async之前加入的任務全部執(zhí)行完成之后(這些任務是并發(fā)執(zhí)行的)
2、再執(zhí)行dispatch_barrier_async中的任務
3、dispatch_barrier_async中的任務執(zhí)行完成之后,再去執(zhí)行在dispatch_barrier_async之后加入到隊列中的任務(這些任務是并發(fā)執(zhí)行的)。
使用場景:多讀單寫
//異步柵欄(多讀單寫場景)
-(void)lnAsyncBarrier{
dispatch_queue_t queue = [self createConcurrentQueue];
NSLog(@"======start=====");
[self addTaskWithAsyncInQueue:queue];
/*
*1、等待dispatch_barrier_async之前的任務全部執(zhí)行完
*2、執(zhí)行dispatch_barrier_async的任務
*3、執(zhí)行dispatch_barrier_async之后的任務
*/
dispatch_barrier_async(queue, ^{
NSLog(@"柵欄方法");
});
dispatch_async(queue, ^{
NSLog(@"任務5開始");
sleep(3);
NSLog(@"任務5結束");
});
dispatch_async(queue, ^{
NSLog(@"任務6開始");
sleep(1);
NSLog(@"任務6結束");
});
NSLog(@"======end=====");
}
- dispatch_group_notify
結合dispatch_group_t一起使用,等待組里的任務全部完成后,調(diào)用dispatch_group_notify的block
使用場景:同時下載多個圖片,所有圖片下載完成之后去更新UI(回到主線程)
//group queue
-(void)lnGroupQueue{
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = [self createConcurrentQueue];
//假設這個數(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(), ^{
// 當添加到組中的所有任務執(zhí)行完成之后會調(diào)用該Block
NSLog(@"所有圖片已全部下載完成");
});
}