隊(duì)列與任務(wù)
任務(wù)task
就是需要執(zhí)行的操作忱反,是GCD中放在block中巾乳,需要在線程中執(zhí)行的那段代碼。
執(zhí)行方式厂僧,有兩種:
-
同步執(zhí)行
把任務(wù)同步添加到指定的隊(duì)列中分瘾。在隊(duì)列中,之前的任務(wù)執(zhí)行結(jié)束之前會一直等待吁系,直到隊(duì)列里面的任務(wù)完成之后,再去執(zhí)行其他的任務(wù)
同步執(zhí)行的任務(wù)只能在當(dāng)前線程中執(zhí)行白魂,不具備開啟新線程的能力汽纤。
-
函數(shù):
dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
// 同步執(zhí)行任務(wù)的創(chuàng)建 dispatch_sync(queue, ^{ // 同步執(zhí)行的任務(wù)代碼 });
-
異步執(zhí)行
異步添加任務(wù)到指定的隊(duì)列中,不需要理會隊(duì)列中其他的任務(wù)福荸。這種任務(wù)無需做任何的等待蕴坪,添加到隊(duì)列就會立即執(zhí)行。
異步執(zhí)行可以在新的線程中執(zhí)行敬锐,具備開啟新的線程的能力背传。
-
函數(shù):
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
// 異步執(zhí)行任務(wù)的創(chuàng)建 dispatch_async(queue, ^{ // 異步執(zhí)行的任務(wù)代碼 });
隊(duì)列
是指執(zhí)行任務(wù)的等待隊(duì)列,是用來存放任務(wù)的台夺,隊(duì)列分為“串行隊(duì)列”和“并發(fā)隊(duì)列”径玖。隊(duì)列采用“先進(jìn)先出”的原則,即新任務(wù)總是被出入到隊(duì)列的末尾颤介,而讀取任務(wù)的時(shí)候總是從隊(duì)列的頭部開始梳星。串行隊(duì)列和并發(fā)隊(duì)列都遵循這個(gè)原則。
隊(duì)列的分類
-
串行隊(duì)列(serial)
只開啟一個(gè)線程
一次只有一個(gè)任務(wù)被執(zhí)行
一個(gè)任務(wù)執(zhí)行完成后滚朵,再去執(zhí)行下一個(gè)任務(wù)
多個(gè)串行隊(duì)列之間是按照并發(fā)隊(duì)列的形式進(jìn)行執(zhí)行
-
并發(fā)隊(duì)列(concurrent)
可以開啟多個(gè)線程
多個(gè)任務(wù)可以同時(shí)執(zhí)行
隊(duì)列的類型以及獲取相應(yīng)的隊(duì)列
主隊(duì)列(Main Dispatch Queue)
專門負(fù)責(zé)調(diào)度主線程的任務(wù)冤灾,沒有辦法開辟新的線程。在主隊(duì)列的任務(wù)辕近,無論是同步任務(wù)還是異步任務(wù)韵吨,都不會開啟新的線程。
-
主線程只能過去移宅,不能創(chuàng)建归粉。獲取方法:
dispatch_queue_main_t dispatch_get_main_queue(void)
//獲取主隊(duì)列 dispatch_queue_t mainQueue = dispatch_get_main_queue();
主隊(duì)列是一種串行隊(duì)列。
-
主隊(duì)列與主線程的關(guān)系
主隊(duì)列的任務(wù)一定在主線程中執(zhí)行
主線程可以執(zhí)行主隊(duì)列以外的其他隊(duì)列的任務(wù)
所有追加到主隊(duì)列的操作都會在runloop中執(zhí)行.
全局并發(fā)隊(duì)列(Global Dispatch Queue
是一種并發(fā)隊(duì)列吞杭,有系統(tǒng)提供盏浇,方便開發(fā)人員編程使用,可以不用創(chuàng)建直接使用芽狗。
并發(fā)的執(zhí)行多個(gè)任務(wù)绢掰,但是任務(wù)的執(zhí)行順序是隨機(jī)的
-
獲取方法:
dispatch_queue_global_t dispatch_get_global_queue(intptr_t identifier, uintptr_t flags);
參數(shù)1: 長整型參數(shù)intptr_t identifier,表示隊(duì)列的優(yōu)先級或者Qos的值
-
參數(shù)2: 長整型參數(shù)uintptr_t flags,傳0就可以滴劲。
//創(chuàng)建全局并發(fā)隊(duì)列 dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
自定義隊(duì)列
如果以上兩種隊(duì)列不能滿足要求的話攻晒,可以自定義隊(duì)列。
串行隊(duì)列班挖、并發(fā)隊(duì)列都可以進(jìn)行自定義
自定義隊(duì)列可以設(shè)置隊(duì)列的優(yōu)先級
-
自定義隊(duì)列需要用到的方法是:
dispatch_queue_t dispatch_queue_create(const char *_Nullable label, dispatch_queue_attr_t _Nullable attr);
參數(shù)1: 隊(duì)列的標(biāo)識符
-
參數(shù)2: 一個(gè)結(jié)合了服務(wù)質(zhì)量Qos級別的隊(duì)列屬性值鲁捏,可以傳以下兩種類型的值
-
表示隊(duì)列類型的值
- 直接傳值NULL: 默認(rèn)創(chuàng)建一個(gè)串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("identifier", NULL);
- DISPATCH_QUEUE_SERIAL: 創(chuàng)建一個(gè)串行隊(duì)列
dispatch_queue_t serialQueue = dispatch_queue_create("identifier", DISPATCH_QUEUE_SERIAL);
- DISPATCH_QUEUE_CONCURRENT: 創(chuàng)建一個(gè)并發(fā)隊(duì)列
dispatch_queue_t conQueue = dispatch_queue_create("identifier", DISPATCH_QUEUE_CONCURRENT);
-
表示執(zhí)行任務(wù)的服務(wù)質(zhì)量Qos級別
- 使用
dispatch_queue_attr_t dispatch_queue_attr_make_with_qos_class(dispatch_queue_attr_t _Nullable attr,dispatch_qos_class_t qos_class, int relative_priority);
//設(shè)置隊(duì)列的類型,設(shè)置隊(duì)列的優(yōu)先級 /* 第二個(gè)參數(shù) __QOS_ENUM(qos_class, unsigned int, QOS_CLASS_USER_INTERACTIVE __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x21, QOS_CLASS_USER_INITIATED __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x19, QOS_CLASS_DEFAULT __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x15, QOS_CLASS_UTILITY __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x11, QOS_CLASS_BACKGROUND __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x09, QOS_CLASS_UNSPECIFIED __QOS_CLASS_AVAILABLE(macos(10.10), ios(8.0)) = 0x00, ); */ dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_DEFAULT, -1); //創(chuàng)建隊(duì)列 dispatch_queue_t queue = dispatch_queue_create("identifier", attr);
- 使用
-
任務(wù)和隊(duì)列的組合萧芙,對線程的影響
任何和隊(duì)列需要搭配使用给梅,算上主線程的特殊情況,總共是有六種組合双揪。
同步任務(wù) + 并發(fā)隊(duì)列
同步任務(wù)不會開啟新的線程动羽,雖然任務(wù)是在并發(fā)隊(duì)列中,但是系統(tǒng)只會使用同步任務(wù)所在的線程渔期,所以這種組合沒有開啟線程运吓,并且任務(wù)按照串行的方式執(zhí)行。
需要說明的是疯趟,這種組合是在當(dāng)前線程執(zhí)行任務(wù)拘哨,不一定是在主線程。
//同步任務(wù) + 并發(fā)隊(duì)列
- (void)concurrentQueueAndSync{
//創(chuàng)建并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("concurrentQueueAndSync", DISPATCH_QUEUE_CONCURRENT);
//創(chuàng)建同步執(zhí)行的任務(wù)1
dispatch_sync(queue, ^{
sleep(1);
NSLog(@"任務(wù)1--同步任務(wù) + 并發(fā)隊(duì)列 -: %@",[NSThread currentThread]);
});
//任務(wù)2
dispatch_sync(queue, ^{
sleep(2);
NSLog(@"任務(wù)2--同步任務(wù) + 并發(fā)隊(duì)列 -: %@",[NSThread currentThread]);
});
//任務(wù)3
dispatch_sync(queue, ^{
sleep(1);
NSLog(@"任務(wù)3--同步任務(wù) + 并發(fā)隊(duì)列 -: %@",[NSThread currentThread]);
});
//任務(wù)4
dispatch_sync(queue, ^{
sleep(2);
NSLog(@"任務(wù)4--同步任務(wù) + 并發(fā)隊(duì)列 -: %@",[NSThread currentThread]);
});
}
打印結(jié)果為:
2022-04-01 16:28:56.369531+0800 suanfaProject[5147:205112] 任務(wù)1--同步任務(wù) + 并發(fā)隊(duì)列 -: <_NSMainThread: 0x60000012c000>{number = 1, name = main}
2022-04-01 16:28:58.370188+0800 suanfaProject[5147:205112] 任務(wù)2--同步任務(wù) + 并發(fā)隊(duì)列 -: <_NSMainThread: 0x60000012c000>{number = 1, name = main}
2022-04-01 16:28:59.371567+0800 suanfaProject[5147:205112] 任務(wù)3--同步任務(wù) + 并發(fā)隊(duì)列 -: <_NSMainThread: 0x60000012c000>{number = 1, name = main}
2022-04-01 16:29:01.373276+0800 suanfaProject[5147:205112] 任務(wù)4--同步任務(wù) + 并發(fā)隊(duì)列 -: <_NSMainThread: 0x60000012c000>{number = 1, name = main}
同步任務(wù) + 串行隊(duì)列
同步任務(wù)不會開啟新的線程信峻,而且串行隊(duì)列也不會開啟新的線程倦青。所以這種組合是不會開啟線程,并且所有的任務(wù)按照串行的方式盹舞,按順序逐個(gè)完成姨夹。
需要說明的是,這種組合是在當(dāng)前線程執(zhí)行任務(wù)矾策,不一定是在主線程磷账。
//同步任務(wù) + 串行隊(duì)列
- (void)serialQueueAndSync{
//創(chuàng)建串行隊(duì)列
dispatch_queue_t serialQueue = dispatch_queue_create("serialQueueAndSync", DISPATCH_QUEUE_SERIAL);
//開啟同步執(zhí)行任務(wù)1
dispatch_sync(serialQueue, ^{
sleep(1);
NSLog(@"任務(wù)1--同步任務(wù) + 串行隊(duì)列 -: %@",[NSThread currentThread]);
});
//任務(wù)2
dispatch_sync(serialQueue, ^{
sleep(2);
NSLog(@"任務(wù)2--同步任務(wù) + 串行隊(duì)列 -: %@",[NSThread currentThread]);
});
//任務(wù)3
dispatch_sync(serialQueue, ^{
sleep(1);
NSLog(@"任務(wù)3--同步任務(wù) + 串行隊(duì)列 -: %@",[NSThread currentThread]);
});
//任務(wù)4
dispatch_sync(serialQueue, ^{
sleep(2);
NSLog(@"任務(wù)4--同步任務(wù) + 串行隊(duì)列 -: %@",[NSThread currentThread]);
});
}
打印結(jié)果為:
2022-04-01 16:37:14.093280+0800 suanfaProject[5295:212370] 任務(wù)1--同步任務(wù) + 串行隊(duì)列 -: <_NSMainThread: 0x600001194940>{number = 1, name = main}
2022-04-01 16:37:16.094911+0800 suanfaProject[5295:212370] 任務(wù)2--同步任務(wù) + 串行隊(duì)列 -: <_NSMainThread: 0x600001194940>{number = 1, name = main}
2022-04-01 16:37:17.095654+0800 suanfaProject[5295:212370] 任務(wù)3--同步任務(wù) + 串行隊(duì)列 -: <_NSMainThread: 0x600001194940>{number = 1, name = main}
2022-04-01 16:37:19.098231+0800 suanfaProject[5295:212370] 任務(wù)4--同步任務(wù) + 串行隊(duì)列 -: <_NSMainThread: 0x600001194940>{number = 1, name = main}
同步任務(wù) + 主隊(duì)列
介紹主隊(duì)列的時(shí)候說過,主隊(duì)列是沒有辦法開辟新的線程贾虽,無論任務(wù)是要同步執(zhí)行還是異步執(zhí)行逃糟,都不會開啟新的線程。所以涉及到主線程的組合蓬豁,是比較特殊的绰咽。
這種情況的組合,會阻塞主線程地粪,導(dǎo)致程序的崩潰取募。究其原因是因?yàn)橹骶€程需要等到主線程的任務(wù)完成之后,才會去執(zhí)行主隊(duì)列的任務(wù)蟆技。而同步任務(wù)(dispatch_sync)函數(shù)中的代碼需要等到執(zhí)行完成才會返回玩敏。這種情況下斗忌,雙方互相等待,也就是主隊(duì)列中的任務(wù)等待主線程執(zhí)行完成旺聚,主隊(duì)列在等待dispatch_sync函數(shù)中的任務(wù)執(zhí)行完成织阳,最終結(jié)果就是導(dǎo)致主線程卡住。
//同步任務(wù) + 主線程主隊(duì)列
-(void)mainQueueAndSync{
//獲取主隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//創(chuàng)建異步任務(wù)
dispatch_sync(mainQueue, ^{
NSLog(@"同步任務(wù) + 主線程主隊(duì)列 -: %@",[NSThread currentThread]);
});
}
異步任務(wù) + 并發(fā)隊(duì)列
異步任務(wù)會開啟新的線程砰粹,并且并發(fā)任務(wù)也會開啟新的線程唧躲。所以這種組合是多個(gè)線程并發(fā)執(zhí)行任務(wù)。
//異步任務(wù) + 并發(fā)隊(duì)列
- (void)concurrentQueueAndAsync{
NSLog(@"當(dāng)前線程 -: %@",[NSThread currentThread]);
//創(chuàng)建并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("concurrentQueueAndAsync", DISPATCH_QUEUE_CONCURRENT);
//創(chuàng)建同步執(zhí)行的任務(wù)1
dispatch_async(queue, ^{
sleep(1);
NSLog(@"任務(wù)1--異步任務(wù) + 并發(fā)隊(duì)列 -: %@",[NSThread currentThread]);
});
//任務(wù)2
dispatch_async(queue, ^{
sleep(1);
NSLog(@"任務(wù)2--異步任務(wù) + 并發(fā)隊(duì)列-: %@",[NSThread currentThread]);
});
//任務(wù)2
dispatch_async(queue, ^{
sleep(2);
NSLog(@"任務(wù)3--異步任務(wù) + 并發(fā)隊(duì)列 -: %@",[NSThread currentThread]);
});
//任務(wù)2
dispatch_async(queue, ^{
sleep(1);
NSLog(@"任務(wù)4--異步任務(wù) + 并發(fā)隊(duì)列 -: %@",[NSThread currentThread]);
});
}
打印結(jié)果為:
2022-04-01 17:00:01.138537+0800 suanfaProject[5589:228584] 當(dāng)前線程 -: <_NSMainThread: 0x6000033dc340>{number = 1, name = main}
2022-04-01 17:00:02.139603+0800 suanfaProject[5589:228706] 任務(wù)2--異步任務(wù) + 并發(fā)隊(duì)列-: <NSThread: 0x6000033d1a00>{number = 7, name = (null)}
2022-04-01 17:00:02.143847+0800 suanfaProject[5589:228709] 任務(wù)1--異步任務(wù) + 并發(fā)隊(duì)列 -: <NSThread: 0x600003394140>{number = 2, name = (null)}
2022-04-01 17:00:02.143849+0800 suanfaProject[5589:228710] 任務(wù)4--異步任務(wù) + 并發(fā)隊(duì)列 -: <NSThread: 0x6000033dd9c0>{number = 4, name = (null)}
2022-04-01 17:00:03.140795+0800 suanfaProject[5589:228708] 任務(wù)3--異步任務(wù) + 并發(fā)隊(duì)列 -: <NSThread: 0x600003393100>{number = 6, name = (null)}
異步任務(wù) + 串行隊(duì)列
異步任務(wù)會開啟線程碱璃,串行隊(duì)列只在新開啟的線程中按照順序執(zhí)行弄痹。
//異步任務(wù) + 串行隊(duì)列
- (void)serialQueueAndAsync{
NSLog(@"當(dāng)前線程 -: %@",[NSThread currentThread]);
//創(chuàng)建串行隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("serialQueueAndAsync", DISPATCH_QUEUE_SERIAL);
//創(chuàng)建異步執(zhí)行任務(wù)1
dispatch_async(queue, ^{
sleep(1);
NSLog(@"任務(wù)1--異步任務(wù) + 串行隊(duì)列 -: %@",[NSThread currentThread]);
});
//任務(wù)2
dispatch_async(queue, ^{
sleep(2);
NSLog(@"任務(wù)2--異步任務(wù) + 串行隊(duì)列 -: %@",[NSThread currentThread]);
});
//任務(wù)3
dispatch_async(queue, ^{
sleep(1);
NSLog(@"任務(wù)3--異步任務(wù) + 串行隊(duì)列 -: %@",[NSThread currentThread]);
});
//任務(wù)4
dispatch_async(queue, ^{
sleep(2);
NSLog(@"任務(wù)4--異步任務(wù) + 串行隊(duì)列 -: %@",[NSThread currentThread]);
});
}
打印結(jié)果為:
2022-04-01 17:03:10.569084+0800 suanfaProject[5627:230899] 當(dāng)前線程 -: <_NSMainThread: 0x600002058900>{number = 1, name = main}
2022-04-01 17:03:11.571632+0800 suanfaProject[5627:231008] 任務(wù)1--異步任務(wù) + 串行隊(duì)列 -: <NSThread: 0x600002000640>{number = 7, name = (null)}
2022-04-01 17:03:13.576875+0800 suanfaProject[5627:231008] 任務(wù)2--異步任務(wù) + 串行隊(duì)列 -: <NSThread: 0x600002000640>{number = 7, name = (null)}
2022-04-01 17:03:14.578503+0800 suanfaProject[5627:231008] 任務(wù)3--異步任務(wù) + 串行隊(duì)列 -: <NSThread: 0x600002000640>{number = 7, name = (null)}
2022-04-01 17:03:16.584066+0800 suanfaProject[5627:231008] 任務(wù)4--異步任務(wù) + 串行隊(duì)列 -: <NSThread: 0x600002000640>{number = 7, name = (null)}
異步任務(wù) + 主隊(duì)列
介紹主隊(duì)列的時(shí)候說過,主隊(duì)列是沒有辦法開辟新的線程嵌器,無論任務(wù)是要同步執(zhí)行還是異步執(zhí)行拼坎,都不會開啟新的線程鳍咱。所以涉及到主線程的組合纪岁,是比較特殊的卒密。
所以這種組合也是不開啟線程庇谆,并且在主線程中按照順序串行執(zhí)行岳掐。
//異步任務(wù) + 主隊(duì)列
-(void)mainQueueAndAsync{
//獲取主隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//創(chuàng)建異步任務(wù)
dispatch_async(mainQueue, ^{
sleep(1);
NSLog(@"1--異步任務(wù) + 主隊(duì)列 -: %@",[NSThread currentThread]);
});
dispatch_async(mainQueue, ^{
sleep(2);
NSLog(@"2--異步任務(wù) + 主隊(duì)列 -: %@",[NSThread currentThread]);
});
dispatch_async(mainQueue, ^{
sleep(1);
NSLog(@"3--異步任務(wù) + 主隊(duì)列 -: %@",[NSThread currentThread]);
});
dispatch_async(mainQueue, ^{
sleep(2);
NSLog(@"4--異步任務(wù) + 主隊(duì)列 -: %@",[NSThread currentThread]);
});
}
打印結(jié)果為:
2022-04-01 17:07:02.329970+0800 suanfaProject[5680:233053] 1--異步任務(wù) + 主隊(duì)列 -: <_NSMainThread: 0x600002ef0340>{number = 1, name = main}
2022-04-01 17:07:04.331448+0800 suanfaProject[5680:233053] 2--異步任務(wù) + 主隊(duì)列 -: <_NSMainThread: 0x600002ef0340>{number = 1, name = main}
2022-04-01 17:07:05.332954+0800 suanfaProject[5680:233053] 3--異步任務(wù) + 主隊(duì)列 -: <_NSMainThread: 0x600002ef0340>{number = 1, name = main}
2022-04-01 17:07:07.333557+0800 suanfaProject[5680:233053] 4--異步任務(wù) + 主隊(duì)列 -: <_NSMainThread: 0x600002ef0340>{number = 1, name = main}
線程間通信
在開發(fā)過程中,主線程進(jìn)行UI刷新饭耳,把圖片下載串述、大量計(jì)算、網(wǎng)絡(luò)請求等一些耗時(shí)的操作放在其他的線程寞肖,當(dāng)這些耗時(shí)的操作完成后需要將數(shù)據(jù)同步給UI纲酗,主線程刷新UI,那么就要用到線程之間的通訊新蟆。
在多個(gè)線程之前進(jìn)行通信時(shí)觅赊,一定要注意線程相互等待導(dǎo)致的死鎖、死循環(huán)等問題琼稻。
//主要是子線程中通信到主線程
-(void)commucationBetweenThreads{
__block NSInteger value = 0;
//創(chuàng)建一個(gè)并發(fā)隊(duì)列或者使用global隊(duì)列
dispatch_queue_t queue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
// dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//創(chuàng)建異步任務(wù)
dispatch_async(queue, ^{
NSLog(@"任務(wù)1----thread: %@",[NSThread currentThread]);
for (int i=0; i<5; i++) {
value += 1;
sleep(1);
}
//創(chuàng)建主隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//創(chuàng)建異步任務(wù)
dispatch_async(mainQueue, ^{
NSLog(@"任務(wù)2----thread: %@,and value is %li",[NSThread currentThread],value);
});
});
}