多線程實(shí)現(xiàn)的幾種方案,主要包括pthread附鸽、NSThread、GCD瞒瘸、NSOperation坷备。PS:其中pthread和NSThread需要我們管理線程生命周期,比較麻煩情臭,不是很常用省撑,我們重點(diǎn)關(guān)注GCD和NSOperation赌蔑。This is GCD。
GCD竟秫,Grand Central Dispath是異步執(zhí)行任務(wù)的技術(shù)之一娃惯,開發(fā)者只需要制定執(zhí)行的任務(wù),將任務(wù)放入隊(duì)列中肥败,GCD會(huì)自動(dòng)在系統(tǒng)中創(chuàng)建線程來計(jì)劃執(zhí)行這些任務(wù)趾浅。 偉大的中心調(diào)度器,聽起來就很牛逼了馒稍,功能也是比較強(qiáng)大的皿哨。
我們知道任務(wù)是放在隊(duì)列中執(zhí)行的,設(shè)置好執(zhí)行的順序是并行還是串行纽谒,然后設(shè)置執(zhí)行方式是同步還是異步就好了证膨,剩下的事情就交給系統(tǒng)進(jìn)行處理了」那【系統(tǒng)是怎么處理的呢央勒?這里先留個(gè)扣子】
- 創(chuàng)建隊(duì)列(隊(duì)列的名稱采用應(yīng)用ID的方式,即按照FQDN-逆序全域名的方式)
// 串行隊(duì)列
dispatch_queue_t serialQueue = dispatch_queue_create("com.silence.gcd.mySerialQueue", DISPATCH_QUEUE_SERIAL);
// 并行隊(duì)列
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.silence.gcd.myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
- 設(shè)置執(zhí)行方式
// 同步執(zhí)行
dispatch_sync(serialQueue, ^{
// 所要執(zhí)行的任務(wù)
NSLog(@"當(dāng)前線程===》%@",[NSThread currentThread]);
});
// 打印信息:當(dāng)前線程===》<NSThread: 0x600003e493c0>{number = 1, name = main}
// 可以看出沒有創(chuàng)建新的線程澳化,默認(rèn)在主線程順序執(zhí)行
// 異步執(zhí)行
dispatch_async(serialQueue, ^{
// 所要執(zhí)行的任務(wù)
NSLog(@"當(dāng)前線程===》%@",[NSThread currentThread]);
});
// 打印信息:當(dāng)前線程===》<NSThread: 0x600001a49a00>{number = 3, name = (null)}
// 可以看出創(chuàng)建了新的線程订歪,在新的線程中執(zhí)行的任務(wù)
- 設(shè)置優(yōu)先級(jí)
這里就說到全局隊(duì)列的一個(gè)特性,設(shè)置優(yōu)先級(jí)肆捕,我們自己創(chuàng)建的隊(duì)列不可以,那么怎么給我們自己創(chuàng)建的隊(duì)列設(shè)置優(yōu)先級(jí)呢盖高?可以將我們創(chuàng)建的隊(duì)列作為全局隊(duì)列的子隊(duì)列慎陵,然后設(shè)置優(yōu)先級(jí)。PS:使用時(shí)盡量用DISPATCH_QUEUE_PRIORITY_DEFAULT喻奥,而且最好不要用這一特性席纽,會(huì)造成優(yōu)先級(jí)倒置的問題。
// 獲取全局隊(duì)列,設(shè)置最低的優(yōu)先級(jí)
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0);
// 創(chuàng)建我們自己的隊(duì)列
dispatch_queue_t serialQueue = dispatch_queue_create("com.silence.tongbu.createGlobalQueue", DISPATCH_QUEUE_SERIAL);
// 將我們的隊(duì)列設(shè)置成為全局隊(duì)列的子隊(duì)列
dispatch_set_target_queue(serialQueue, globalQueue);
dispatch_async(serialQueue, ^{
NSLog(@"任務(wù)1:當(dāng)前線程==》%@",[NSThread currentThread]);
});
// 設(shè)置最高優(yōu)先級(jí)
dispatch_queue_t serialQueue2 = dispatch_queue_create("com.silence.tongbu.createGlobalQueue2", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(serialQueue2, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));
dispatch_async(serialQueue2, ^{
NSLog(@"任務(wù)2:當(dāng)前線程==》%@",[NSThread currentThread]);
});
2019-05-30 11:57:53.972219+0800 同步[13224:1596637] 任務(wù)2:當(dāng)前線程==》<NSThread: 0x6000031ec8c0>{number = 3, name = (null)}
2019-05-30 11:57:53.972237+0800 同步[13224:1596638] 任務(wù)1:當(dāng)前線程==》<NSThread: 0x6000031fed80>{number = 4, name = (null)}
4.延遲執(zhí)行dispatch_after(dispatch_time_t when, dispatch_queue_t queue,dispatch_block_t block);
參數(shù)when延遲多久撞蚕,queue追加到那個(gè)隊(duì)列中润梯,block執(zhí)行什么任務(wù)。
// 設(shè)置延遲多久執(zhí)行
dispatch_time_t timeQueue = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
NSLog(@"我要潛水3秒鐘,waiting...");
dispatch_after(timeQueue, dispatch_get_main_queue(), ^{
NSLog(@"我來也......^v^");
});
- 隊(duì)列組Queue Group甥厦,解決執(zhí)行多個(gè)并行隊(duì)列任務(wù)時(shí)纺铭,想在全部執(zhí)行結(jié)束后執(zhí)行結(jié)束處理。
// 隊(duì)列組刀疙,主要是為了想再多個(gè)并行任務(wù)都執(zhí)行完成之后舶赔,執(zhí)行結(jié)束操作
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_t queueGroup = dispatch_group_create();
dispatch_sync(globalQueue, ^{
NSLog(@"task 1");
});
dispatch_sync(globalQueue, ^{
NSLog(@"task 2");
});
// 所有的隊(duì)列任務(wù)都完成之后,執(zhí)行結(jié)束操作
dispatch_group_notify(queueGroup, globalQueue, ^{
NSLog(@"ALL tasks are done");
});
6.信號(hào)量(旗語)Semaphore谦秧,是一種控制可訪問資源的信號(hào)標(biāo)識(shí)竟纳,根據(jù)我們?cè)O(shè)定的信號(hào)計(jì)數(shù)撵溃,系統(tǒng)分配執(zhí)行的線程數(shù)。
如何使用信號(hào)量锥累? 先創(chuàng)建一個(gè)信號(hào)量缘挑,然后等待信號(hào)(線程等待),最后信號(hào)來了桶略,喚醒等待的線程语淘。
1>創(chuàng)建信號(hào)量,參數(shù):信號(hào)計(jì)數(shù)的個(gè)數(shù)
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
2>等待(減少)信號(hào)
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
3>(增加)信號(hào)來了删性,喚醒等待程序
dispatch_semaphore_signal(semaphore);
PS:執(zhí)行一個(gè)同步任務(wù)亏娜,只有兩個(gè)2資源,只能創(chuàng)建2個(gè)線程蹬挺,怎么執(zhí)行三個(gè)任務(wù)维贺?
// 設(shè)置信號(hào)量的數(shù)量為2,同時(shí)只能開啟2個(gè)線程
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.silence.gcd.myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
// 等待(減少)信號(hào)量巴帮。 *減少計(jì)數(shù)信號(hào)量溯泣。 如果結(jié)果值小于零,此功能在返回前等待信號(hào)發(fā)生榕茧。
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"Task 1");
// 信號(hào)來了垃沦,可以通行。 *增加計(jì)數(shù)信號(hào)量用押。 如果前一個(gè)值小于零肢簿,此函數(shù)在返回之前喚醒等待線程。
dispatch_semaphore_signal(semaphore);
});
dispatch_async(concurrentQueue, ^{
// 等待(減少)信號(hào)
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"Task 2");
// (增加)信號(hào)來了蜻拨,喚醒等待信號(hào)
dispatch_semaphore_signal(semaphore);
});
dispatch_async(concurrentQueue, ^{
NSLog(@"Task 3");
});
// 打印信息
2019-06-03 15:41:11.748542+0800 semaphore[25788:4575492] Task 1
2019-06-03 15:41:11.748603+0800 semaphore[25788:4575494] Task 3
2019-06-03 15:41:11.748680+0800 semaphore[25788:4575493] Task 2
7.使用場景1池充,GCD加載耗時(shí)任務(wù),完成之后返回到主線程
dispatch_queue_t queue = dispatch_queue_create("com.silence.tongbu.mySerialQueue",DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
// 耗時(shí)操作缎讼,加載一張圖片
NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1523902227523&di=474686c26c7a7d2b815305dd45f0e046&imgtype=0&src=http%3A%2F%2Fcdnq.duitang.com%2Fuploads%2Fitem%2F201504%2F30%2F20150430125352_aeTLk.jpeg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 加載完成之后收夸,回到主線程,將圖片顯示出來
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
使用場景2血崭,并行隊(duì)列任務(wù)中卧惜,想實(shí)現(xiàn)任務(wù)A和任務(wù)C完成之后,再執(zhí)行任務(wù)B
- 實(shí)現(xiàn)方式1夹纫,使用柵欄函數(shù)(就像一道柵欄一樣將任務(wù)隔開咽瓷,柵欄函數(shù)之前的任務(wù)完成之后,再執(zhí)行柵欄之后的任務(wù))
UIImage * (^loadPicTask) (void) = ^(){
// 耗時(shí)操作舰讹,加載一張圖片
NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1523902227523&di=474686c26c7a7d2b815305dd45f0e046&imgtype=0&src=http%3A%2F%2Fcdnq.duitang.com%2Fuploads%2Fitem%2F201504%2F30%2F20150430125352_aeTLk.jpeg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
return image;
};
// 通過柵欄函數(shù)的方式忱详,先實(shí)現(xiàn)任務(wù)A和任務(wù)C完成之后,再執(zhí)行任務(wù)B
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.silence.barrier.myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
// 任務(wù)A
dispatch_async(concurrentQueue, ^{
NSLog(@"任務(wù)A:獲取圖片==》%@跺涤,當(dāng)前線程==》%@",loadPicTask(),[NSThread currentThread]);
});
// 任務(wù)C
dispatch_async(concurrentQueue, ^{
// 耗時(shí)操作匈睁,加載一張圖片
NSLog(@"任務(wù)C:獲取圖片==》%@监透,當(dāng)前線程==》%@",loadPicTask(),[NSThread currentThread]);
});
// 柵欄函數(shù)
dispatch_barrier_async(concurrentQueue, ^{
// 這里省略了一萬字.......
NSLog(@"------------------------這是著名的柵欄(函數(shù)),這之前的先執(zhí)行航唆,這之后的后執(zhí)行----------------------------------------");
});
// 任務(wù)B
dispatch_async(concurrentQueue, ^{
// 耗時(shí)操作胀蛮,加載一張圖片
NSLog(@"任務(wù)B:獲取圖片==》%@,當(dāng)前線程==》%@",loadPicTask(),[NSThread currentThread]);
});
// 打印結(jié)果如下糯钙,情況1
// 任務(wù)C:獲取圖片==》<UIImage: 0x6000019c18f0>, {1200, 1200}粪狼,當(dāng)前線程==》<NSThread: 0x60000278df40>{number = 3, name = (null)}
// 任務(wù)A:獲取圖片==》<UIImage: 0x6000019c5260>, {1200, 1200},當(dāng)前線程==》<NSThread: 0x60000279f440>{number = 4, name = (null)}
// ------------------------這是著名的柵欄(函數(shù))任岸,這之前的先執(zhí)行再榄,這之后的后執(zhí)行----------------------------------------
// 任務(wù)B:獲取圖片==》<UIImage: 0x6000019cda40>, {1200, 1200},當(dāng)前線程==》<NSThread: 0x60000279f440>{number = 4, name = (null)}
// 打印結(jié)果如下享潜,情況2
// 任務(wù)A:獲取圖片==》<UIImage: 0x60000341e3e0>, {1200, 1200}困鸥,當(dāng)前線程==》<NSThread: 0x600000a46740>{number = 3, name = (null)}
// 任務(wù)C:獲取圖片==》<UIImage: 0x60000341e3e0>, {1200, 1200},當(dāng)前線程==》<NSThread: 0x600000a74a00>{number = 4, name = (null)}
// ------------------------這是著名的柵欄(函數(shù))剑按,這之前的先執(zhí)行疾就,這之后的后執(zhí)行----------------------------------------
// 任務(wù)B:獲取圖片==》<UIImage: 0x60000341e3e0>, {1200, 1200},當(dāng)前線程==》<NSThread: 0x600000a74a00>{number = 4, name = (null)}
// 不管你打印的是那種結(jié)果艺蝴,任務(wù)B總是在任務(wù)A和任務(wù)C之后執(zhí)行猬腰。
- 實(shí)現(xiàn)方式2,使用信號(hào)量猜敢,通過設(shè)置信號(hào)計(jì)數(shù)為2姑荷,這樣每次只能執(zhí)行2個(gè)任務(wù)(A和C),最后在執(zhí)行任務(wù)B
// 通過信號(hào)量的方式缩擂,先實(shí)現(xiàn)任務(wù)A和任務(wù)C完成之后鼠冕,再執(zhí)行任務(wù)B
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.silence.barrier.myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
// 任務(wù)A
dispatch_async(concurrentQueue, ^{
// 等待(減少)信號(hào)
dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
NSLog(@"任務(wù)A:獲取圖片==》%@,當(dāng)前線程==》%@",loadPicTask(),[NSThread currentThread]);
// (增加)信號(hào)來了撇叁,喚醒等待線程
dispatch_semaphore_signal(semaphore);
});
// 任務(wù)C
dispatch_async(concurrentQueue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 耗時(shí)操作,加載一張圖片
NSLog(@"任務(wù)C:獲取圖片==》%@畦贸,當(dāng)前線程==》%@",loadPicTask(),[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
// 任務(wù)B
dispatch_async(concurrentQueue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 耗時(shí)操作陨闹,加載一張圖片
NSLog(@"任務(wù)B:獲取圖片==》%@,當(dāng)前線程==》%@",loadPicTask(),[NSThread currentThread]);
dispatch_semaphore_signal(semaphore);
});
// 打印結(jié)果如下
2019-06-03 16:16:51.676707+0800 semaphore[26364:4699323] 任務(wù)C:獲取圖片==》<UIImage: 0x60000285e610>, {1200, 1200}薄坏,當(dāng)前線程==》<NSThread: 0x600001632180>{number = 3, name = (null)}
2019-06-03 16:16:51.746762+0800 semaphore[26364:4699325] 任務(wù)A:獲取圖片==》<UIImage: 0x600002840000>, {1200, 1200}趋厉,當(dāng)前線程==》<NSThread: 0x600001631f80>{number = 4, name = (null)}
2019-06-03 16:16:51.936077+0800 semaphore[26364:4699322] 任務(wù)B:獲取圖片==》<UIImage: 0x600002853020>, {1200, 1200},當(dāng)前線程==》<NSThread: 0x60000160de80>{number = 5, name = (null)}
- 實(shí)現(xiàn)方式3胶坠,使用dispatch_group_t調(diào)度組(隊(duì)列組)方式君账,將任務(wù)A和任務(wù)C放在一個(gè)調(diào)度組中執(zhí)行,任務(wù)B在該調(diào)度組執(zhí)行完成之后執(zhí)行沈善。
// 通過隊(duì)列組的方式乡数,先實(shí)現(xiàn)任務(wù)A和任務(wù)C完成之后椭蹄,再執(zhí)行任務(wù)B
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.silence.barrier.myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
// 創(chuàng)建隊(duì)列組
dispatch_group_t queueGroup = dispatch_group_create();
// 任務(wù)A
dispatch_group_async(queueGroup, concurrentQueue, ^{
NSLog(@"任務(wù)A:獲取圖片==》%@,當(dāng)前線程==》%@",loadPicTask(),[NSThread currentThread]);
});
// 任務(wù)C
dispatch_group_async(queueGroup,concurrentQueue, ^{
NSLog(@"任務(wù)C:獲取圖片==》%@净赴,當(dāng)前線程==》%@",loadPicTask(),[NSThread currentThread]);
});
// 等收到A,C任務(wù)都執(zhí)行完成之后绳矩,再任務(wù)B
dispatch_group_notify(queueGroup,concurrentQueue, ^{
NSLog(@"任務(wù)B:獲取圖片==》%@,當(dāng)前線程==》%@",loadPicTask(),[NSThread currentThread]);
});
關(guān)鍵字和概念可以參考:
OC的多線程1————關(guān)鍵詞 以及 解決方案