1.GCD簡介
Grand Central Dispatch(GCD) 是 Apple 開發(fā)的一個多核編程的較新的解決方法博个。它主要用于優(yōu)化應用程序以支持多核處理器以及其他對稱多處理系統(tǒng)。它是一個在線程池模式的基礎上執(zhí)行的并發(fā)任務功偿。在 Mac OS X 10.6 雪豹中首次推出盆佣,也可在 iOS 4 及以上版本使用。
GCD優(yōu)點
- GCD可用于多核的并行運算
- GCD會利用更多的CPU內(nèi)核
- GCD會自動管理線程的生命周期
2.GCD任務和隊列
同步執(zhí)行(sync):
- 同步添加到指定的隊列中械荷,再添加的任務執(zhí)行結束前共耍,一直等待,在隊列里的任務執(zhí)行完成后在執(zhí)行
- 不具備開啟線程的能力吨瞎,在當前線程中執(zhí)行痹兜。
異步執(zhí)行:
- 異步任務添加到隊列中,不會等待颤诀,可以繼續(xù)執(zhí)行字旭。
- 可以下新的線程中執(zhí)行任務。
隊列: 用來存放任務的隊列崖叫。隊列是一種特殊的線性表遗淳,采用FIFO(先進先出)的原則。
- 串行隊列:每次只有一個任務會被執(zhí)行心傀,當一個任務執(zhí)行完成后在執(zhí)行下一個任務
- 并發(fā)隊列:可以讓多個任務同時執(zhí)行屈暗。可以開啟多個線程脂男,同時執(zhí)行任務恐锦。
3.創(chuàng)建隊列
- 串行隊列
/*
*串行隊列
*參數(shù)1:線程名稱可以為空測試時可用
*參數(shù)2:串行隊列
*/
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
- 并發(fā)隊列
/*
*并發(fā)隊列
*參數(shù)1:線程名稱可以為空測試時可用
*參數(shù)2:并發(fā)隊列
*/
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
- 對于串行隊列,GCD提供了一種特殊的串行隊列主隊列(Main Dispatch Queue)
- 所有放到主隊列中的任務疆液,都會放到主線程中執(zhí)行一铅。
- dispatch_get_main_queue()獲取主隊列。
dispatch_queue_t queue = dispatch_get_main_queue();
- 并發(fā)隊列堕油,GCD默認提供了全局并發(fā)隊列
/*
*參數(shù)一 表示線程優(yōu)先級
*參數(shù)二 0即可
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
4.GCD的基本使用
- 同步執(zhí)行 + 并發(fā)隊列:
不會開啟新的線程潘飘,在當前線程執(zhí)行,執(zhí)行完一個任務后在執(zhí)行下一個掉缺。
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程
NSLog(@"syncConcurrent---begin");
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2----%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3----%@",[NSThread currentThread]);
}
});
NSLog(@"syncConcurrent---end");
//打印信息
2018-12-10 15:21:27.866589+0800 GCD[11895:5222420] currentThread---<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:27.866688+0800 GCD[11895:5222420] syncConcurrent---begin
2018-12-10 15:21:29.867784+0800 GCD[11895:5222420] 1----<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:31.869445+0800 GCD[11895:5222420] 1----<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:33.871222+0800 GCD[11895:5222420] 2----<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:35.872828+0800 GCD[11895:5222420] 2----<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:37.874443+0800 GCD[11895:5222420] 3----<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:39.876046+0800 GCD[11895:5222420] 3----<NSThread: 0x1c0066c80>{number = 1, name = main}
2018-12-10 15:21:39.876239+0800 GCD[11895:5222420] syncConcurrent---end
- 異步執(zhí)行+并發(fā)隊列:
會開啟新線程卜录,所有任務同步執(zhí)行。
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程
NSLog(@"syncConcurrent---begin");
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2----%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3----%@",[NSThread currentThread]);
}
});
NSLog(@"syncConcurrent---end");
//打印信息
2018-12-10 15:22:58.596939+0800 GCD[11898:5223017] currentThread---<NSThread: 0x1c4069140>{number = 1, name = main}
2018-12-10 15:22:58.597029+0800 GCD[11898:5223017] syncConcurrent---begin
2018-12-10 15:22:58.597099+0800 GCD[11898:5223017] syncConcurrent---end
2018-12-10 15:23:00.603383+0800 GCD[11898:5223043] 2----<NSThread: 0x1c4075ac0>{number = 4, name = (null)}
2018-12-10 15:23:00.603419+0800 GCD[11898:5223041] 1----<NSThread: 0x1c0467340>{number = 3, name = (null)}
2018-12-10 15:23:00.603645+0800 GCD[11898:5223042] 3----<NSThread: 0x1c407af40>{number = 5, name = (null)}
2018-12-10 15:23:02.606575+0800 GCD[11898:5223042] 3----<NSThread: 0x1c407af40>{number = 5, name = (null)}
2018-12-10 15:23:02.608954+0800 GCD[11898:5223043] 2----<NSThread: 0x1c4075ac0>{number = 4, name = (null)}
2018-12-10 15:23:02.609031+0800 GCD[11898:5223041] 1----<NSThread: 0x1c0467340>{number = 3, name = (null)}
- 同步執(zhí)行+串行隊列
不會開啟新的線程眶明,在當前線程執(zhí)行任務艰毒,任務是串行,在執(zhí)行完成后一個任務在執(zhí)行下一個任務搜囱。
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程
NSLog(@"syncConcurrent---begin");
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2----%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3----%@",[NSThread currentThread]);
}
});
NSLog(@"syncConcurrent---end");
//打印信息
2018-12-10 15:25:54.043186+0800 GCD[11901:5223884] currentThread---<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:25:54.043275+0800 GCD[11901:5223884] syncConcurrent---begin
2018-12-10 15:25:56.044897+0800 GCD[11901:5223884] 1----<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:25:58.046774+0800 GCD[11901:5223884] 1----<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:26:00.048408+0800 GCD[11901:5223884] 2----<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:26:02.050100+0800 GCD[11901:5223884] 2----<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:26:04.051480+0800 GCD[11901:5223884] 3----<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:26:06.054114+0800 GCD[11901:5223884] 3----<NSThread: 0x1c007f000>{number = 1, name = main}
2018-12-10 15:26:06.054304+0800 GCD[11901:5223884] syncConcurrent---end
- 異步執(zhí)行 + 串行隊列
會開啟新的線程丑瞧,但是任務是串行執(zhí)行柑土,執(zhí)行完成一個任務在執(zhí)行下一個任務。
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當前線程
NSLog(@"syncConcurrent---begin");
dispatch_queue_t queue = dispatch_queue_create("", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2----%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i<2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3----%@",[NSThread currentThread]);
}
});
NSLog(@"syncConcurrent---end");
//打印信息
2018-12-10 15:27:46.578929+0800 GCD[11904:5224555] currentThread---<NSThread: 0x1c007f840>{number = 1, name = main}
2018-12-10 15:27:46.579018+0800 GCD[11904:5224555] syncConcurrent---begin
2018-12-10 15:27:46.579133+0800 GCD[11904:5224555] syncConcurrent---end
2018-12-10 15:27:48.585620+0800 GCD[11904:5224577] 1----<NSThread: 0x1c0479980>{number = 3, name = (null)}
2018-12-10 15:27:50.590468+0800 GCD[11904:5224577] 1----<NSThread: 0x1c0479980>{number = 3, name = (null)}
2018-12-10 15:27:52.596004+0800 GCD[11904:5224577] 2----<NSThread: 0x1c0479980>{number = 3, name = (null)}
2018-12-10 15:27:54.601597+0800 GCD[11904:5224577] 2----<NSThread: 0x1c0479980>{number = 3, name = (null)}
2018-12-10 15:27:56.607246+0800 GCD[11904:5224577] 3----<NSThread: 0x1c0479980>{number = 3, name = (null)}
2018-12-10 15:27:58.610236+0800 GCD[11904:5224577] 3----<NSThread: 0x1c0479980>{number = 3, name = (null)}
- 同步執(zhí)行 + 主隊列(在主線程):
出現(xiàn)死鎖绊汹,相互等待卡主不執(zhí)行稽屏。syncMain任務放到主線程中隊列執(zhí)行。同步執(zhí)行
會等待當前隊列中的任務執(zhí)行完成后才會執(zhí)行 - 異步執(zhí)行 + 主隊列 :
在主線程中執(zhí)行任務西乖,執(zhí)行完成一個任務后再執(zhí)行下一個任務狐榔。
5.GCD信號量
當我們在進行網(wǎng)絡請求是,需要在一個請求執(zhí)行完成后再去執(zhí)行下一個請求是获雕,因為網(wǎng)絡請求是異步執(zhí)行薄腻,需要用到信號量。當信號量為0的時候就會阻塞線程届案,大于0的時候就不會阻塞線程被廓,通過改變信號量的值就可以控制線程。
dispatch_semaphore_create
創(chuàng)建信號量dispatch_semaphore_signal
1.返回值long
類型萝玷,當返回值為0時,表示當前沒有線程等待處理的信號昆婿,信號量增加1球碉。
2.當返回值不為0時,表示當前有一個會多個線程等待處理的信號量仓蛆,并且該函數(shù)喚醒了一個等待的線程睁冬。dispatch_semaphore_wait
1.等待信號,判斷信號量是否大于0看疙,如果大于0就減掉1個信號往下執(zhí)行豆拨。
2.如果等于0函數(shù)就會阻塞當前的線程。
//創(chuàng)建信號量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
NSLog(@"開始網(wǎng)絡請求");
[[SetNetWorking baseHttpManager] upLoadDataImage:image successBlock:^(id response) {
//信號量+1
NSLog(@"正在網(wǎng)絡請求");
dispatch_semaphore_signal(semaphore);
if (response) {
}else{
}
} failedBlock:^(NSError * _Nonnull error) {
dispatch_semaphore_signal(semaphore);
[ToastView toastWithNSString:@"上傳圖片失敗"];
}];
//信號量為0的時候回阻塞線程
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"網(wǎng)絡請求完成");
//打印信息
6.GCD 隊列組:dispatch_group
- 創(chuàng)建隊列組
dispatch_group_t group = dispatch_group_create();
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"Request_1");
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"Request_2");
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"Request_3");
});
//隊列組執(zhí)行完后執(zhí)行
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"任務均完成能庆,刷新界面");
});
但是在進行網(wǎng)絡請求是施禾,請求都是異步執(zhí)行,要實現(xiàn)在所有請求執(zhí)行完成后再執(zhí)行后面的任務需要用到
dispatch_group_enter(group)
和dispatch_group_leave(group)
-
dispatch_group_enter
標志著一個任務追加到 group搁胆,執(zhí)行一次弥搞,相當于 group 中未執(zhí)行完畢任務數(shù)+1 -
dispatch_group_leave
標志著一個任務離開了 group,執(zhí)行一次渠旁,相當于 group 中未執(zhí)行完畢任務數(shù)-1 - 當 group 中未執(zhí)行完畢任務數(shù)為0的時候攀例,才會使
dispatch_group_wait
解除阻塞,以及執(zhí)行追加到dispatch_group_notify
中的任務
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//請求1
[網(wǎng)絡請求:{
成功:dispatch_group_leave(group);
失敗:dispatch_group_leave(group);
}];
});
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[網(wǎng)絡請求:{
成功:dispatch_group_leave(group);
失敗:dispatch_group_leave(group);
}];
});
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[網(wǎng)絡請求:{
成功:dispatch_group_leave(group);
失敗:dispatch_group_leave(group);
}];
});
//隊列組執(zhí)行完后執(zhí)行
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"任務均完成顾腊,刷新界面");
});
-
dispatch_group_wait
會阻塞當前線程粤铭,當組中多有任務完成后再執(zhí)行下面任務
GCD柵欄
dispatch_barrier_async
在異步執(zhí)行兩組操作,需要實現(xiàn)在執(zhí)行完一組后在執(zhí)行一組是需要使用到柵欄
7.GCD定時器
__block NSInteger time = 10; //倒計時時間
//創(chuàng)建全局隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//使用全局隊列創(chuàng)建計時器
dispatch_source_t sourceTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
/*
*參數(shù)一:傳入計時器
*參數(shù)二:計時器開始時間
*參數(shù)三:計時器間隔時間
*參數(shù)四:代表精準度杂靶,0是最精確
*/
dispatch_source_set_timer(sourceTimer, DISPATCH_TIME_NOW, 1.0*NSEC_PER_SEC, 0);
//開始執(zhí)行計時器任務
dispatch_source_set_event_handler(sourceTimer, ^{
if (time<=0) {
//計時器關閉
dispatch_source_cancel(sourceTimer);
NSLog(@"執(zhí)行完成");
}else{
NSLog(@"%ld",time);
}
time --;
});
//開始執(zhí)行計時器任務
dispatch_resume(sourceTimer);
8.GCD一次性代碼只執(zhí)行一次梆惯。
- 我們在創(chuàng)建單例酱鸭、或者有整個程序運行過程中只執(zhí)行一次的代碼時,我們就用到了 GCD 的
dispatch_once
函數(shù)加袋。使用
dispatch_once
函數(shù)能保證某段代碼在程序運行過程中只被執(zhí)行1次凛辣,并且即使在多線程的環(huán)境下,dispatch_once
也可以保證線程安全职烧。
/**
* 一次性代碼(只執(zhí)行一次)dispatch_once
*/
- (void)once {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執(zhí)行1次的代碼(這里面默認是線程安全的)
});
}