只需知道如何開線程、會看相關的第三方框架即可。
簡介
-
兩個核心概念
任務:執(zhí)行什么操作
隊列:用來存放任務
隊列類型:
并發(fā)隊列
(Concurrent Dispatch Queue)
可以讓多個任務并發(fā)執(zhí)行(自動開啟多個線程同時執(zhí)行任務)
在異步dispatch_async函數(shù)下才有效串行隊列
(Serial Dispatch Queue)
讓任務一個接著一個地執(zhí)行(一個任務執(zhí)行完畢后,再執(zhí)行下一個)
按照FIFO順序執(zhí)行。
任務派發(fā):
- 同步
指阻塞當前線程,既要等待添加的耗時任務塊Block完成后,函數(shù)才能返回,后面的代碼才能繼續(xù)執(zhí)行。 - 異步
指將任務添加到隊列后,函數(shù)立即返回,后面的代碼不用等待添加的任務完成即可馬上執(zhí)行。異步提交無法確定任務執(zhí)行順序。
GCD多線程經(jīng)常會使用 同步函數(shù)dispatch_sync、異步函數(shù)dispatch_async,向指定隊列 添加任務。
分辨
-
各種隊列的執(zhí)行效果
-
主隊列(一種串行隊列)
凡是放到主隊列中的任務,都必須在主線程中執(zhí)行。
1.異步函數(shù)+主隊列
串行執(zhí)行,所有任務都在主線程中執(zhí)行,不會開線程
2.同步函數(shù)+主隊列————》會死鎖!一般不用。
主隊列的特點:如果主隊列發(fā)現(xiàn)當前主線程有任務在執(zhí)行,那么它會暫停隊列中的任務,直到主線程空閑為止。
GCD實現(xiàn)線程間通信
說明:在更新UI時回到了主線程(通過新設一個同步/異步函數(shù),隊列參數(shù)為主隊列)
- 開子線程做異步任務
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//...做一些任務
dispatch_sync(dispatch_get_main_queue(), ^{
//回到主線程,更新UI
});
});
舉例,開啟子線程下載圖片
//1.獲取一個 全局串行隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
__weak typeof(self) weakSelf = self;
//2.把任務添加到隊列中執(zhí)行
dispatch_async(queue, ^{
NSURL *url = [NSURL URLWithString:_imgString];
NSData *imageData = [NSData dataWithContentsOfURL:url];
UIImage * bgImage = [UIImage imageWithData:imageData];
bgImage = [bgImage applyLightEffectAtFrame:CGRectMake(0, 0, bgImage.size.width, bgImage.size.height)];
//3.回到主線程
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.backImageView.image = bgImage;
});
});
GCD常用函數(shù)
- 延遲執(zhí)行
int64_t delayInSeconds = 10.0; // 延遲的時間
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
// do something ... ...
});
或
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)( 10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// ...
});
- 一次性代碼
這個應用程序只執(zhí)行一次,不再執(zhí)行
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//...
});
不能應用于懶加載。應用于單例模式。
- 柵欄函數(shù)
用于控制異步函數(shù)的執(zhí)行順序,只有執(zhí)行完柵欄函數(shù),才能執(zhí)行后面的隊列任務。使用時,必須自己創(chuàng)建隊列,不能使用全局并發(fā)隊列。
同步函數(shù)沒有必要用柵欄函數(shù)。
- 快速迭代(遍歷)
內(nèi)部會開子線程和主線程一起完成遍歷任務,異步執(zhí)行任務
- 主線程異步任務
dispatch_async(dispatch_get_main_queue(), ^{
});
- 柵欄函數(shù)
用于控制異步函數(shù)的執(zhí)行順序??杀苊鈹?shù)據(jù)競爭。使用時,必須自己創(chuàng)建隊列,不能使用全局并發(fā)隊列。
//創(chuàng)建隊列
dispatch_queue_t queue = dispatch_queue_create("download", DISPATCH_QUEUE_CONCURRENT);
//異步函數(shù)
dispatch_async(queue, ^{
//....異步任務1
});
dispatch_async(queue, ^{
//....異步任務2
});
//柵欄函數(shù)
dispatch_barrier_async(queue, ^{
//...
});
一個dispatch barrier允許在一個并發(fā)隊列中創(chuàng)建一個同步點。當在并發(fā)隊列中遇到一個barrier, 他會延遲執(zhí)行barrier的block,等待所有在barrier之前提交的blocks執(zhí)行結束。 這時,barrier block自己開始執(zhí)行。 之后, 隊列繼續(xù)正常的執(zhí)行操作。
- 隊列組
某幾個隊列的任務都完成后,再執(zhí)行其他任務
//創(chuàng)建隊列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
/創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
//...異步任務1
});
dispatch_group_async(group, queue, ^{
//...異步任務2
});
dispatch_group_async(group, queue, ^{
//...異步任務3
});
// 攔截通知,當隊列組里的任務都完成了,會進入下面這個方法
dispatch_group_notify(group, queue, ^{
//... ...
});
如,加載多個圖片
// 使用Dispatch Group追加block到Global Group Queue,這些block如果全部執(zhí)行完畢,
就會執(zhí)行Main Dispatch Queue中的結束處理的block。
// 創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();
// 獲取全局并發(fā)隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{ /*加載圖片1 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片2 */ });
dispatch_group_async(group, queue, ^{ /*加載圖片3 */ });
// 當并發(fā)隊列組中的任務執(zhí)行完畢后才會執(zhí)行這里的代碼
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 合并圖片
});
- 定時任務
/* 在指定線程上定義計時器 */
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
/* 開始的時間 */
dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
/* 設置計時器 ,時間間隔 5s */
dispatch_source_set_timer(_timer, when, 5.0 * NSEC_PER_SEC, 0);
/* 計時器回調(diào)block */
dispatch_source_set_event_handler(_timer, ^{
// do something
NSLog(@"需要做的任務!");
});
/* 開啟計時器 */
dispatch_resume(_timer);
self.timer = _timer; // 強引用計時器對象(必須)
必須銷毀self.timer才能終止定時任務:
self.timer = nil ;
GCD取消線程
可以取消還未執(zhí)行的線程。但是沒辦法做到取消一個正在執(zhí)行的線程。
- 取消函數(shù) dispatch_block_cancel
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_block_t block1 = dispatch_block_create(0, ^{
NSLog(@"block1");
});
dispatch_block_t block2 = dispatch_block_create(0, ^{
NSLog(@"block2");
});
dispatch_block_t block3 = dispatch_block_create(0, ^{
NSLog(@"block3");
});
dispatch_async(queue, block1);
dispatch_async(queue, block2);
dispatch_async(queue, block3);
dispatch_block_cancel(block3); // 取消 block3
投機取巧:使用臨時變量 + return 方式取消 正在執(zhí)行的Block。
__block BOOL gcdFlag = NO; // 臨時變量
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (long i=0; i<1000; i++) {
NSLog(@"正在執(zhí)行第i次:%ld",i);
sleep(0.1);
if (gcdFlag==YES) { // 判斷并終止
NSLog(@"終止");
return ;
}
};
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"我要停止啦");
gcdFlag = YES;
});


