[iOS多線程中用僧,隊(duì)列和執(zhí)行的排列組合結(jié)果分析]
多線程中的隊(duì)列有:串行隊(duì)列,并發(fā)隊(duì)列辰如,全局隊(duì)列普监,主隊(duì)列。
執(zhí)行的方法有:同步執(zhí)行和異步執(zhí)行丧没。那么兩兩一組合會(huì)有哪些注意事項(xiàng)呢鹰椒?
提到多線程,也就是四種呕童,pthread漆际,NSthread,GCD夺饲,NSOperation
其中phtread是跨平臺(tái)的奸汇。GCD和NSOperation都是常用的,后者是基于前者的往声。
但是兩者區(qū)別:GCD的核心概念是將一個(gè)任務(wù)添加到隊(duì)列擂找,指定任務(wù)執(zhí)行的方法,然后執(zhí)行浩销。 NSOperation則是直接將一個(gè)操作添加到隊(duì)列中贯涎。
為了整體結(jié)構(gòu)更加清晰,我是用GCD來(lái)做此排列組合的實(shí)驗(yàn)慢洋。實(shí)驗(yàn)主要是通過(guò)循環(huán)內(nèi)打印和主線程的打印先后順序來(lái)判斷結(jié)果塘雳,最后再加以總結(jié)
1.串行隊(duì)列,同步執(zhí)行
dispatch_queue_t q = dispatch_queue_create("dantesx", NULL);
// 執(zhí)行任務(wù)
for (int i = 0; i<10; i++) {
dispatch_sync(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"董鉑然 come here");
運(yùn)行效果:
執(zhí)行結(jié)果可以清楚的看到全在主線程執(zhí)行普筹,并且是按照數(shù)序執(zhí)行败明,循環(huán)結(jié)束之后主線程的打印才輸出。
2.串行隊(duì)列太防,異步執(zhí)行
dispatch_queue_t q = dispatch_queue_create("dantesx", NULL);
for (int i = 0; i<10; i++) {
dispatch_async(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
// [NSThread sleepForTimeInterval:0.001];
NSLog(@"董鉑然 come here");
運(yùn)行結(jié)果
結(jié)果顯示妻顶,系統(tǒng)開(kāi)了1條異步線程,因此全部在線程2執(zhí)行,并且是順序執(zhí)行讳嘱。主線程打印雖然在最上面幔嗦,但是這個(gè)先后順序是不確定,如果睡個(gè)0.001秒沥潭,主線程的打印會(huì)混在中間崭添。
3.并發(fā)隊(duì)列,異步執(zhí)行
// 1. 隊(duì)列
dispatch_queue_t q = dispatch_queue_create("dantesx", DISPATCH_QUEUE_CONCURRENT);
// 2. 異步執(zhí)行
for (int i = 0; i<10; i++) {
dispatch_async(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
// [NSThread sleepForTimeInterval:2.0];
NSLog(@"董鉑然 come here");
運(yùn)行結(jié)果
結(jié)果顯示叛氨,主線程的打印還是混在中間不確定的,因?yàn)楫惒骄€程就是誰(shuí)也不等誰(shuí)棘伴。系統(tǒng)開(kāi)了多條線程寞埠,并且執(zhí)行的順序也是亂序的
4.并發(fā)隊(duì)列,同步執(zhí)行
// 1. 隊(duì)列
dispatch_queue_t q = dispatch_queue_create("dantesx", DISPATCH_QUEUE_CONCURRENT);
// 2. 同步執(zhí)行
for (int i = 0; i<10; i++) {
dispatch_sync(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
// [NSThread sleepForTimeInterval:2.0];
NSLog(@"董鉑然 come here");
運(yùn)行結(jié)果
這個(gè)運(yùn)行結(jié)果和第1種的串行隊(duì)列焊夸,同步執(zhí)行是一模一樣的仁连。 因?yàn)橥饺蝿?wù)的概念就是按順序執(zhí)行,后面都要等阱穗。言外之意就是不允許多開(kāi)線程饭冬。 同步和異步則是決定開(kāi)一條還是開(kāi)多條。
所以一旦是同步執(zhí)行揪阶,前面什么隊(duì)列已經(jīng)沒(méi)區(qū)別了昌抠。
5.主隊(duì)列,異步執(zhí)行
// 1. 主隊(duì)列 - 程序啟動(dòng)之后已經(jīng)存在主線程鲁僚,主隊(duì)列同樣存在
dispatch_queue_t q = dispatch_get_main_queue();
// 2. 安排一個(gè)任務(wù)
for (int i = 0; i<10; i++) {
dispatch_async(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"睡會(huì)");
[NSThread sleepForTimeInterval:2.0];
NSLog(@"董鉑然 come here");
運(yùn)行結(jié)果
結(jié)果顯示有點(diǎn)出人意料炊苫。異步主隊(duì)列是不會(huì)開(kāi)啟新線程。主線程在睡會(huì)之后才打印冰沙,循環(huán)一直在等著侨艾。因?yàn)橹麝?duì)列的任務(wù)雖然會(huì)加到主線程中執(zhí)行,但是如果主線程里也有任務(wù)就必須等主線程任務(wù)執(zhí)行完才輪到主隊(duì)列的拓挥。
6.主隊(duì)列唠梨,同步執(zhí)行
dispatch_queue_t q = dispatch_get_main_queue();
NSLog(@"卡死了嗎?");
dispatch_sync(q, ^{
NSLog(@"我來(lái)了");
});
NSLog(@"董鉑然 come here");
運(yùn)行結(jié)果為卡死
卡死的原因是循環(huán)等待侥啤,主隊(duì)列的東西要等主線程執(zhí)行完当叭,而因?yàn)槭峭綀?zhí)行不能開(kāi)線程,所以下面的任務(wù)要等上面的任務(wù)執(zhí)行完愿棋,所以卡死科展。這是排列組合中唯一一個(gè)會(huì)卡死的組合。
7.同步任務(wù)的使用場(chǎng)景
dispatch_queue_t q = dispatch_queue_create("dantesx", DISPATCH_QUEUE_CONCURRENT);
// 1. 用戶登錄糠雨,必須要第一個(gè)執(zhí)行
dispatch_sync(q, ^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"用戶登錄 %@", [NSThread currentThread]);
});
// 2. 扣費(fèi)
dispatch_async(q, ^{
NSLog(@"扣費(fèi) %@", [NSThread currentThread]);
});
// 3. 下載
dispatch_async(q, ^{
NSLog(@"下載 %@", [NSThread currentThread]);
});
NSLog(@"董鉑然 come here");
運(yùn)行結(jié)果
結(jié)果顯示才睹,“用戶登陸”在主線程打印,后兩個(gè)在異步線程打印。上面的“用戶登陸”使用同步執(zhí)行琅攘,后面的扣費(fèi)和下載都是異步執(zhí)行垮庐。所以“用戶登陸”必須第一個(gè)打印出來(lái)不管等多久,然后后面的兩個(gè)異步和主線程打印會(huì)不確定順序的打印坞琴。這就是日常開(kāi)發(fā)中哨查,那些后面對(duì)其有依賴的必須要先執(zhí)行的任務(wù)使用同步執(zhí)行,然后反正都要執(zhí)行先后順序無(wú)所謂的使用異步執(zhí)行剧辐。
8.block異步任務(wù)包裹同步任務(wù)
dispatch_queue_t q = dispatch_queue_create("dantesx", DISPATCH_QUEUE_CONCURRENT);
void (^task)() = ^ {
// 1. 用戶登錄寒亥,必須要第一個(gè)執(zhí)行
dispatch_sync(q, ^{
NSLog(@"用戶登錄 %@", [NSThread currentThread]);
});
// 2. 扣費(fèi)
dispatch_async(q, ^{
NSLog(@"扣費(fèi) %@", [NSThread currentThread]);
});
// 3. 下載
dispatch_async(q, ^{
NSLog(@"下載 %@", [NSThread currentThread]);
});
};
dispatch_async(q, task);
[NSThread sleepForTimeInterval:1.0];
NSLog(@"董鉑然 come here");
運(yùn)行結(jié)果
因?yàn)檎麄€(gè)block是在異步執(zhí)行的,所以即使里面“用戶登陸”是同步執(zhí)行荧关,那也無(wú)法在主線程中執(zhí)行溉奕,只能開(kāi)新的線程執(zhí)行(這里可能是多條),因?yàn)槭峭降乃员仨毜人葓?zhí)行忍啤,后面的“扣費(fèi)”和“下載”在上面同步執(zhí)行結(jié)束之后加勤,不確定順序的打印。
9.全局隊(duì)列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; i++) {
dispatch_async(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
[NSThread sleepForTimeInterval:1.0];
NSLog(@"com here");
運(yùn)行結(jié)果
全局隊(duì)列的本質(zhì)就是并發(fā)隊(duì)列同波,只是在后面加入了鳄梅,“服務(wù)質(zhì)量”,和“調(diào)度優(yōu)先級(jí)” 兩個(gè)參數(shù)未檩,這兩個(gè)參數(shù)一般為了系統(tǒng)間的適配戴尸,最好直接填0和0。
如果不是在董鉑然博客園看到這邊文章請(qǐng) 點(diǎn)擊查看原文
總結(jié):
1. 開(kāi)不開(kāi)線程讹挎,取決于執(zhí)行任務(wù)的函數(shù)校赤,同步不開(kāi),異步開(kāi)筒溃。
2. 開(kāi)幾條線程马篮,取決于隊(duì)列,串行開(kāi)一條怜奖,并發(fā)開(kāi)多條(異步)
3. 主隊(duì)列: 專門用來(lái)在主線程上調(diào)度任務(wù)的"隊(duì)列"浑测,主隊(duì)列不能在其他線程中調(diào)度任務(wù)!
4. 如果主線程上當(dāng)前正在有執(zhí)行的任務(wù)歪玲,主隊(duì)列暫時(shí)不會(huì)調(diào)度任務(wù)的執(zhí)行迁央!主隊(duì)列同步任務(wù),會(huì)造成死鎖滥崩。原因是循環(huán)等待
5. 同步任務(wù)可以隊(duì)列調(diào)度多個(gè)異步任務(wù)前岖圈,指定一個(gè)同步任務(wù),讓所有的異步任務(wù)钙皮,等待同步任務(wù)執(zhí)行完成蜂科,這是依賴關(guān)系顽决。
6. 全局隊(duì)列:并發(fā),能夠調(diào)度多個(gè)線程导匣,執(zhí)行效率高才菠,但是相對(duì)費(fèi)電。 串行隊(duì)列效率較低贡定,省電省流量赋访,或者是任務(wù)之間需要依賴也可以使用串行隊(duì)列。
7. 也可以通過(guò)判斷當(dāng)前用戶的網(wǎng)絡(luò)環(huán)境來(lái)決定開(kāi)的線程數(shù)缓待。WIFI下6條蚓耽,3G/4G下2~3條。
本文轉(zhuǎn)載董鉑然博客園看到這邊文章請(qǐng) 點(diǎn)擊查看原文