計(jì)算機(jī)多進(jìn)程發(fā)展的背景:
早期計(jì)算機(jī)工作是單道批處理程序處理,講寫好的作業(yè)卡依次放進(jìn)計(jì)算機(jī)的批處理程序執(zhí)行诅迷,效率非常低翁锡,后面出現(xiàn)了多道批處理程序,同一段時(shí)間允許多個(gè)程序同事快速執(zhí)行附帽,雖然效率很高埠戳,但是后面隨著硬件技術(shù)的跟進(jìn),除了分時(shí)系統(tǒng)處理作業(yè)這是已經(jīng)成為是進(jìn)程作業(yè)蕉扮;根據(jù)每個(gè)進(jìn)程的時(shí)間分片整胃,同一時(shí)間里快速切換執(zhí)行。后面又出現(xiàn)實(shí)時(shí)系統(tǒng)喳钟,更加高效屁使。。奔则。
多線程概念:
多線程是指在軟件或者硬件條件下可以實(shí)現(xiàn)多個(gè)線程并發(fā)執(zhí)行的技術(shù)蛮寂。具有多線程能力的計(jì)算機(jī)因有硬件的支持可以在同一時(shí)間執(zhí)行多個(gè)線程,進(jìn)而提高計(jì)算機(jī)的處理能力易茬。
多線程于進(jìn)程的關(guān)系:
進(jìn)程是程序執(zhí)行的基本單元酬蹋,而線程是程序執(zhí)行的基本單位及老,一個(gè)進(jìn)程可以擁有多個(gè)線程,并至少擁有一個(gè)線程范抓,每個(gè)線程獨(dú)立執(zhí)行骄恶,多個(gè)線程可以共享它所在進(jìn)程的代碼、數(shù)據(jù)匕垫、進(jìn)程空間和對(duì)應(yīng)的堆空間僧鲁;
多線程對(duì)程序開發(fā)帶來的便利:
- 單線程在遇到耗時(shí)操作的時(shí)候會(huì)阻塞UI線程,用戶界面的鼠標(biāo)點(diǎn)擊年缎、手勢(shì)滾動(dòng)都會(huì)立刻進(jìn)行執(zhí)行悔捶,造成了很不好的用戶體驗(yàn),多線程技術(shù)允許將耗時(shí)操作放進(jìn)單獨(dú)的線程里區(qū)執(zhí)行单芜,從而不會(huì)影響UI線程的交互蜕该,提高用戶體驗(yàn);
- 相對(duì)進(jìn)程來說洲鸠,多線程的創(chuàng)建堂淡、切換所需要cpu消耗、內(nèi)存占用量都會(huì)很少扒腕;
- 對(duì)于多核的操作系統(tǒng)绢淀,單一線程無法很好使用的多核資源,造成資源浪費(fèi)瘾腰。
多線程引進(jìn)的問題:
- 實(shí)現(xiàn)多線程的代碼會(huì)更加復(fù)雜皆的、難度、維護(hù)起來難蹋盆;
- 對(duì)于模型共享數(shù)據(jù)來時(shí)是多個(gè)線程共享的费薄,可能會(huì)出現(xiàn)線程死鎖的問題;
- 由于線程的創(chuàng)建栖雾、調(diào)度楞抡、切換都會(huì)消耗一定的cpu計(jì)算資源、內(nèi)存資源析藕、指令調(diào)度資源召廷,瘋狂的創(chuàng)建多線程會(huì)起到適得其反的后果;
- 數(shù)據(jù)不安全账胧,對(duì)于同一個(gè)共享區(qū)域的數(shù)據(jù)竞慢,可能會(huì)被篡改;
iOS開發(fā)過程中多線程實(shí)現(xiàn)的方案:
基于c語(yǔ)言的POSIX線程治泥,成為pthread 線程梗顺;是操作系統(tǒng)的多線程實(shí)現(xiàn)方案,由于oc中可以實(shí)現(xiàn)也可以使用c混編车摄,posix線程在iOS開發(fā)中也可以使用寺谤;posix線程的優(yōu)點(diǎn)是較為底層性能會(huì)高效仑鸥,但是由于c語(yǔ)言開發(fā),所以需要我們手動(dòng)的管理線程的內(nèi)容变屁,對(duì)于開發(fā)效率是一種不利眼俊;
nsthread 線程基于c語(yǔ)言封裝、面向?qū)ο蟮亩嗑€程實(shí)現(xiàn)方案粟关,api如下:
1. init 方法實(shí)現(xiàn)多線程創(chuàng)建疮胖,該方法需要我們手動(dòng)執(zhí)行strart指令,將它放進(jìn)帶線程就緒隊(duì)列闷板,等待系統(tǒng)自己出發(fā)澎灸;
2. detachNewThreadSelector 方法創(chuàng)建并自動(dòng)放到線程就緒隊(duì)列等待系統(tǒng)自動(dòng)去出發(fā);
2.1 遮晚、 nsthread 優(yōu)點(diǎn):
1. nsthread提供適合oc開發(fā)人員使用多線程實(shí)現(xiàn)api,降低開發(fā)門檻性昭;
2. nsthread 提供了一套工具酷,我們可以快捷拿到多線程的信息和狀態(tài)县遣,例如: 是否是主線程糜颠、是否被取消,是否正在運(yùn)行等
2.2 萧求、nsthread 缺點(diǎn)
1. nsthread 的最大缺點(diǎn)是線程之間的通信比較麻煩其兴,也是設(shè)計(jì)的一種缺陷;
GCD
- 概念
gcd 我們稱為中央調(diào)度管理夸政,基于c接口元旬,是apple公司為了線程不能充分利用多核資源引入的一個(gè)多線程實(shí)現(xiàn)方案;它的工作流程是將一個(gè)事件分成若干小任務(wù)放入串行或者并發(fā)執(zhí)行隊(duì)列守问,遵循先進(jìn)先出的規(guī)則去加入線程執(zhí)行單元匀归,然后執(zhí)行其同步或者異步的執(zhí)行;
1.1 : 優(yōu)點(diǎn):
高效: 基于mach內(nèi)核線程調(diào)度酪碘;
線程安全:不用開發(fā)者關(guān)心線程的內(nèi)存問題朋譬;
使用簡(jiǎn)單盐茎,少代碼解決大問題兴垦;
- 核心機(jī)制-隊(duì)列
gcd 的核心內(nèi)容是隊(duì)列
gcd 的隊(duì)列分為: 串行、并發(fā)字柠、主隊(duì)列探越、全局隊(duì)列、組隊(duì)列
- 串行/并發(fā)/同步異步 組合方式對(duì)應(yīng)的結(jié)果
1. 主線程的時(shí)間優(yōu)先
2. 串行同步執(zhí)行: 執(zhí)行完一個(gè)任務(wù)窑业,再執(zhí)行下一個(gè)任務(wù)钦幔。不開啟新線程,都在主線程執(zhí)行常柄。
3. 串行異步執(zhí)行: 開啟新線程鲤氢,但因?yàn)槿蝿?wù)是串行的搀擂,所以還是按順序執(zhí)行任務(wù)。
4. 并發(fā)隊(duì)列異步執(zhí)行: 并發(fā)執(zhí)行卷玉,會(huì)開啟新的線程哨颂。
5. 并發(fā)隊(duì)列同步執(zhí)行; 按順序執(zhí)行相种,不會(huì)開啟新的線程威恼。
6. 主隊(duì)列同步事件:
7. 主隊(duì)列異步事件: 按次序執(zhí)行。
- gcd 的特別函數(shù):
4.1 欄柵函數(shù) dispatch_barrier_(a)sync
當(dāng)我們需要異步事件同步去執(zhí)行的時(shí)候我們需要用函數(shù) dispatch_barrier_sync
dispatch_barrier_async 異步執(zhí)行
4.2 信號(hào)量
dispatch_semaphore 為了解決共享資源競(jìng)爭(zhēng)問題寝并,可以用信號(hào)量來決定資源是否可以被訪問箫措。
dispatch_semphore_create 創(chuàng)建一個(gè)信號(hào)量,參數(shù)是最大可以同事訪問的線程衬潦;
dispatch_semphore_waite 對(duì)信號(hào)量執(zhí)行減1操作斤蔓,如果結(jié)果大于0,就可以直接進(jìn)行資源訪問别渔;
dispatch_semphone_sign 對(duì)信號(hào)變量執(zhí)行加一操作附迷,訪問完成就要釋放對(duì)資源的占用權(quán)利,即信號(hào)量加1哎媚,不影響其他線程訪問喇伯。
4.3 延遲執(zhí)行函數(shù)
dispatch_after_t 函數(shù) 參數(shù): dispatch_time_t dispatch_queue_t block
4.4 組變量
當(dāng)我們需要多個(gè)請(qǐng)求后才能進(jìn)入下一步時(shí),我們需要下面的函數(shù)\
dispatch_group_t
1. 創(chuàng)建一個(gè)全局的隊(duì)列拨与,默認(rèn)優(yōu)先級(jí)
2. 創(chuàng)建一個(gè)group_t
3. 創(chuàng)建group任務(wù)加入到全局隊(duì)列
4. 監(jiān)聽組隊(duì)列完成的notify dispatch_group_notify
// 加載圖片例子:
NSLog(@"全部開始-----%@", [NSThread currentThread]);
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_async(group, queue, ^{
sleep(4);
NSLog(@"子線程1-----%@", [NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
sleep(3);
NSLog(@"子線程2-----%@", [NSThread currentThread]);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"全部結(jié)束-----%@", [NSThread currentThread]);
});
4.5 dispatch_group_enter/dispatch_group_leave 函數(shù)
4.4的任務(wù)是組任務(wù)力里只是簡(jiǎn)單的輸出一句話稻据,如果在里面加入一個(gè)異步請(qǐng)求就出出現(xiàn)問題:
有問題的代碼:
NSLog(@"全部開始-----%@", [NSThread currentThread]);
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.example.gcdDemo", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(4);
NSLog(@"模擬請(qǐng)求1-----%@", [NSThread currentThread]);
});
});
dispatch_group_async(group, queue, ^{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(3);
NSLog(@"模擬請(qǐng)求2-----%@", [NSThread currentThread]);
});
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"全部結(jié)束-----%@", [NSThread currentThread]);
});
它打印log: 全部開始-全部結(jié)束-任務(wù)2,任務(wù)1买喧, 我們發(fā)現(xiàn)這樣的話與我們的要求的不一樣捻悯,我們要求的是所有的任務(wù)都結(jié)束才會(huì)執(zhí)行notify 函數(shù)但是它提前執(zhí)行啦。
為了解決上面的問題需要用到 dispatch_group_enter/dispatch_group_leave
在組組任務(wù)內(nèi)部淤毛,每次進(jìn)行異步操作之前今缚,先執(zhí)行 dispatch_group_enter函數(shù),參數(shù)是group變量低淡; 當(dāng)內(nèi)部的任務(wù)完成時(shí)執(zhí)行dispatch_group_leave()
代碼如下:
NSLog(@"全部開始-----%@", [NSThread currentThread]);
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.example.gcdDemo", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(4);
NSLog(@"模擬請(qǐng)求1-----%@", [NSThread currentThread]);
dispatch_group_leave(group);
});
});
dispatch_group_async(group, queue, ^{
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
sleep(3);
NSLog(@"模擬請(qǐng)求2-----%@", [NSThread currentThread]);
dispatch_group_leave(group);
});
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"全部結(jié)束-----%@", [NSThread currentThread]);
});
4.6 dispatch_apply 快蘇迭代函數(shù) 參數(shù): 迭代的次數(shù)姓言,所在的隊(duì)列,任務(wù)
4.7 dispatch_once : 只執(zhí)行一次任務(wù)蔗蹋,通常用在單例何荚,
5.gcd的常見問題
5.1 線程死鎖問題
原因:隊(duì)列引起- 場(chǎng)景:在主線程里執(zhí)行同步將任務(wù)添加到主隊(duì)列里
例子: 加入在viewdidload 方法中 我們同步執(zhí)行將block(執(zhí)行一句日志)添加祝隊(duì)列,在這易操作后面加上一個(gè)結(jié)束日志猪杭,運(yùn)行發(fā)現(xiàn)餐塘,block的日志、結(jié)束日志都沒有打印皂吮,通過現(xiàn)象說明是block來不及執(zhí)行戒傻、結(jié)束日志也無法執(zhí)行税手。原因是,首先程序首先將同步執(zhí)行函數(shù)加入到線程執(zhí)行單元里需纳,然后根據(jù)同步執(zhí)行函數(shù)要求將block加入到執(zhí)行主隊(duì)列里冈止,由于是同步函數(shù),只能等到block結(jié)束候齿,同步函數(shù)才能返回熙暴,而block在主隊(duì)列,它的機(jī)制是只有上一個(gè)出隊(duì)列的任務(wù)完成它才可以出隊(duì)列慌盯,這樣同步函數(shù)不能返回導(dǎo)致結(jié)束就無法按順序打印周霉,block無法執(zhí)行,導(dǎo)致同步函數(shù)無法返回亚皂,系統(tǒng)無法進(jìn)入下一步俱箱,就發(fā)錯(cuò)-死鎖。