首先來(lái)了解什么是多線(xiàn)程,進(jìn)程和線(xiàn)程的區(qū)別.
進(jìn)程:
正在進(jìn)行中的程序被稱(chēng)為進(jìn)程,負(fù)責(zé)程序運(yùn)行的內(nèi)存分配;
每一個(gè)進(jìn)程都有自己獨(dú)立的虛擬內(nèi)存空間.
線(xiàn)程:(主線(xiàn)程最大占1M的棧區(qū)空間,每條子線(xiàn)程最大占512K的棧區(qū)空間)
線(xiàn)程是進(jìn)程中一個(gè)獨(dú)立的執(zhí)行路徑(控制單元);
一個(gè)進(jìn)程中至少包含一條線(xiàn)程,即主線(xiàn)程;
可以將耗時(shí)的執(zhí)行路徑(如網(wǎng)絡(luò)請(qǐng)求)放在其他線(xiàn)程中執(zhí)行;
線(xiàn)程不能被殺掉,但是可以暫停/休眠一條線(xiàn)程.
創(chuàng)建線(xiàn)程的目的:
開(kāi)啟一條新的執(zhí)行路徑,運(yùn)行指定的代碼,與主線(xiàn)程中的代碼實(shí)現(xiàn)同時(shí)運(yùn)行.
多任務(wù)調(diào)度系統(tǒng):
每個(gè)應(yīng)用程序由操作系統(tǒng)分配的短暫的時(shí)間片(Timeslice)輪流使用CPU,由于CPU對(duì)每個(gè)時(shí)間片的處理速度非???因此,用戶(hù)看來(lái)這些任務(wù)好像是同時(shí)執(zhí)行的.
并發(fā):
指兩個(gè)或多個(gè)任務(wù)在同一時(shí)間間隔內(nèi)發(fā)生,但是,在任意一個(gè)時(shí)間點(diǎn)上,CPU只會(huì)處理一個(gè)任務(wù).
多線(xiàn)程的優(yōu)勢(shì):
1> 充分發(fā)揮多核處理器優(yōu)勢(shì),將不同線(xiàn)程任務(wù)分配給不同的處理器,真正進(jìn)入"并行運(yùn)算"狀態(tài);
2> 將耗時(shí)的任務(wù)分配到其他線(xiàn)程執(zhí)行,由主線(xiàn)程負(fù)責(zé)統(tǒng)一更新界面會(huì)使應(yīng)用程序更加流暢,用戶(hù)體驗(yàn)更好;
3> 當(dāng)硬件處理器的數(shù)量增加,程序會(huì)運(yùn)行更快,而程序無(wú)需做任何調(diào)整.
弊端:
新建線(xiàn)程會(huì)消耗內(nèi)存空間和CPU時(shí)間,線(xiàn)程太多會(huì)降低系統(tǒng)的運(yùn)行性能.
1.NSThread:
1> 使用NSThread對(duì)象建立一個(gè)線(xiàn)程非常方便;
2> 但是!要使用NSThread管理多個(gè)線(xiàn)程非常困難,不推薦使用;
3> 技巧!使用[NSThread currentThread]跟蹤任務(wù)所在線(xiàn)程,適用于這三種技術(shù).
2.NSOperation/NSOperationQueue:
1> 是使用GCD實(shí)現(xiàn)的一套Objective-C的API;
2> 是面向?qū)ο蟮亩嗑€(xiàn)程技術(shù);
3> 提供了一些在GCD中不容易實(shí)現(xiàn)的特性,如:限制最大并發(fā)數(shù)量,操作之間的依賴(lài)關(guān)系.
3.GCD---Grand Central Dispatch:
1> 是基于C語(yǔ)言的底層API;
2> 用Block定義任務(wù),使用起來(lái)非常靈活便捷;
3> 提供了更多的控制能力以及操作隊(duì)列中所不能使用的底層函數(shù).
iOS的開(kāi)發(fā)者需要了解三種多線(xiàn)程技術(shù)的基本使用,因?yàn)樵趯?shí)際開(kāi)發(fā)中會(huì)根據(jù)實(shí)際情況選擇不同的多線(xiàn)程技術(shù).
GCD的基本思想就是將操作S放在隊(duì)列S中去執(zhí)行.
1> 操作使用Blocks定義;
2> 隊(duì)列負(fù)責(zé)調(diào)度任務(wù)執(zhí)行所在的線(xiàn)程以及具體的執(zhí)行時(shí)間;
3> 隊(duì)列的特點(diǎn)是先進(jìn)先出(FIFO)的,新添加至隊(duì)列的操作都會(huì)排在隊(duì)尾.
提示:
GCD的函數(shù)都是以dispatch(分派/調(diào)度)開(kāi)頭的.
dispatch_queue_t
串行隊(duì)列: 隊(duì)列中的任務(wù)只會(huì)順序執(zhí)行;
并行隊(duì)列: 隊(duì)列中的任務(wù)通常會(huì)并發(fā)執(zhí)行.
dispatch_async 異步操作,會(huì)并發(fā)執(zhí)行,無(wú)法確定任務(wù)的執(zhí)行順序;
dispatch_sync 同步操作,會(huì)依次順序執(zhí)行,能夠決定任務(wù)的執(zhí)行順序.
隊(duì)列不是線(xiàn)程,也不表示對(duì)應(yīng)的CPU.隊(duì)列就是負(fù)責(zé)調(diào)度的.多線(xiàn)程技術(shù)的目的,就是為了在一個(gè)CPU上實(shí)現(xiàn)快速切換!
在串行隊(duì)列中:
同步操作不會(huì)新建線(xiàn)程,操作順序執(zhí)行(沒(méi)用!);
異步操作會(huì)新建線(xiàn)程,操作順序執(zhí)行(非常有用!) (應(yīng)用場(chǎng)景:既不影響主線(xiàn)程,又需要順序執(zhí)行的操作).
在并行隊(duì)列中:
同步操作不會(huì)新建線(xiàn)程,操作順序執(zhí)行;
異步操作會(huì)新建多個(gè)線(xiàn)程,操作無(wú)序執(zhí)行(有用,容易出錯(cuò)),隊(duì)列前如果有其他任務(wù),會(huì)等待前面的任務(wù)完成之后再執(zhí)行.應(yīng)用場(chǎng)景:既不影響主線(xiàn)程,又不需要順序執(zhí)行的操作.
全局隊(duì)列:
全局隊(duì)列是系統(tǒng)的,直接拿過(guò)來(lái)(GET)用就可以,與并行對(duì)立類(lèi)似,但調(diào)試時(shí),無(wú)法確認(rèn)操作所在隊(duì)列.
主隊(duì)列:
每一個(gè)應(yīng)用程序都對(duì)應(yīng)唯一一個(gè)主隊(duì)列,直接GET即可,在多線(xiàn)程開(kāi)發(fā)中,使用主隊(duì)列更新UI;
注意:
主隊(duì)列中的操作都應(yīng)該在主線(xiàn)程上順序執(zhí)行,不存在異步的概念.
如果把主線(xiàn)程中的操作看作是一個(gè)大的Block,那么除非主線(xiàn)程被用戶(hù)殺掉,否則永遠(yuǎn)不會(huì)結(jié)束.所以主隊(duì)列中添加的同步操作永遠(yuǎn)不會(huì)被執(zhí)行,會(huì)死鎖.
1 2 3 4 5 6 7 8 | // 全局隊(duì)列,都在主線(xiàn)程上執(zhí)行,不會(huì)死鎖 dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 并行隊(duì)列,都在主線(xiàn)程上執(zhí)行,不會(huì)死鎖 dispatch_queue_t q = dispatch_queue_create( "m.baidu.com" , DISPATCH_QUEUE_CONCURRENT); // 串行隊(duì)列,會(huì)死鎖,但是會(huì)執(zhí)行嵌套同步操作之前的代碼 dispatch_queue_t q = dispatch_queue_create( "m.baidu.com" , DISPATCH_QUEUE_SERIAL); // 直接死鎖 dispatch_queue_t q = dispatch_get_main_queue(); |
阻塞并行隊(duì)列的執(zhí)行,要求某一操作執(zhí)行后再進(jìn)行后續(xù)操作,如用戶(hù)登錄.
確保塊代碼之外的局部變量確實(shí)被修改.
[NSThread sleepForTimeInterval:2.0f] 通常在多線(xiàn)程調(diào)試中用于模擬耗時(shí)操作,在發(fā)布的應(yīng)用程序中,不要使用此方法!
無(wú)論什么隊(duì)列和什么任務(wù),線(xiàn)程的創(chuàng)建和回收都不需要程序員參與.線(xiàn)程的創(chuàng)建回收工作是由隊(duì)列負(fù)責(zé)的.
1> 通過(guò)GCD,開(kāi)發(fā)者不用再直接跟線(xiàn)程打交道,只需要向隊(duì)列中添加代碼塊即可.
2> GCD在后端管理著一個(gè)線(xiàn)程池,GCD不僅決定著代碼塊將在哪個(gè)線(xiàn)程被執(zhí)行,它還根據(jù)可用的系統(tǒng)資源對(duì)這些線(xiàn)程進(jìn)行管理,從而讓開(kāi)發(fā)者從線(xiàn)程管理的工作中解放出來(lái);通過(guò)集中的管理線(xiàn)程,緩解大量線(xiàn)程被創(chuàng)建的問(wèn)題.
3> 使用GCD,開(kāi)發(fā)者可以將工作考慮為一個(gè)隊(duì)列,而不是一堆線(xiàn)程,這種并行的抽象模型更容易掌握和使用.
蘋(píng)果官方給出的GCD隊(duì)列示意圖:
從中可以看出: GCD公開(kāi)有5個(gè)不同的隊(duì)列:運(yùn)行在主線(xiàn)程中的主隊(duì)列,3個(gè)不同優(yōu)先級(jí)的后臺(tái)隊(duì)列以及一個(gè)優(yōu)先級(jí)更低的后臺(tái)隊(duì)列(用于I/O).
自定義隊(duì)列:串行和并行隊(duì)列.自定義隊(duì)列非常強(qiáng)大,建議在開(kāi)發(fā)中使用.
在自定義隊(duì)列中被調(diào)度的所有Block最終都將被放入到系統(tǒng)的全局隊(duì)列中和線(xiàn)程池中.
提示:
不建議使用不同優(yōu)先級(jí)的隊(duì)列,因?yàn)槿绻O(shè)計(jì)不當(dāng),可能會(huì)出現(xiàn)優(yōu)先級(jí)反轉(zhuǎn),即低優(yōu)先級(jí)的操作阻塞高優(yōu)先級(jí)的操作.
1> NSOperationQueue(操作隊(duì)列)是由GCD提供的隊(duì)列模型的Cocoa抽象,是一套Objective-C的API;
2> GCD提供了更加底層的控制,而NSOperationQueue(操作隊(duì)列)則在GCD之上實(shí)現(xiàn)了一些方便的功能,這些功能對(duì)開(kāi)發(fā)者而言通常是最好最安全的選擇.
NSOperationQueue有兩種不同類(lèi)型的隊(duì)列:主隊(duì)列和自定義隊(duì)列.
主隊(duì)列運(yùn)行在主線(xiàn)程上,自定義隊(duì)列在后臺(tái)執(zhí)行.
隊(duì)列處理的任務(wù)是NSOperation的子類(lèi):NSInvocationOperation 和 NSBlockOperation.
NSOperation的基本使用步驟:
定義操作隊(duì)列 --> 定義操作 -->將操作添加到隊(duì)列.
提示:
一旦將操作添加到隊(duì)列,操作就會(huì)立即被調(diào)度執(zhí)行.
1> 定義隊(duì)列:
2> 操作調(diào)用的方法:
3> 定義操作并添加到隊(duì)列:
提示:需要準(zhǔn)備一個(gè)被調(diào)度的方法,并且能夠接收一個(gè)參數(shù).
定義操作并添加到隊(duì)列:
NSBlockOperation比NSInvocationOperation更加靈活;
設(shè)置操作的依賴(lài)關(guān)系:
利用 " addDependency "可以指定操作之間彼此的依賴(lài)關(guān)系(執(zhí)行先后順序),但是注意不要出現(xiàn)循環(huán)依賴(lài).
設(shè)置同時(shí)并發(fā)的線(xiàn)程數(shù)量:
從本質(zhì)上看,操作隊(duì)列的性能會(huì)比GCD略低,不過(guò),大多數(shù)情況下這點(diǎn)負(fù)面影響可以忽略不計(jì).操作隊(duì)列是并發(fā)編程的首選工具.
在這里,推薦一個(gè)非常好用的第三方編程框架AFN,底層用GCD開(kāi)發(fā),開(kāi)發(fā)的接口是NSOperation的.
多線(xiàn)程中得循環(huán)引用問(wèn)題:
如果self對(duì)象持有操作對(duì)象的引用,同時(shí)操作對(duì)象當(dāng)中又直接訪問(wèn)了self時(shí),才會(huì)造成循環(huán)引用.
單純?cè)诓僮鲗?duì)象中使用self不會(huì)造成循環(huán)引用.
注意: 此時(shí)不要使用[weakSelf].
多線(xiàn)程中的資源共享問(wèn)題:
并發(fā)編程中許多問(wèn)題的根源就是在多線(xiàn)程中訪問(wèn)共享資源.資源可以是一個(gè)屬性,一個(gè)對(duì)象,網(wǎng)絡(luò)設(shè)備或者一個(gè)文件等.
在多線(xiàn)程中任何一個(gè)共享的資源都可能是一個(gè)潛在的沖突點(diǎn),必須精心設(shè)計(jì)以防止這種沖突的發(fā)生.
為了保證性能,atomic僅針對(duì)屬性的setter方法做了保護(hù).
爭(zhēng)搶共享資源時(shí),如果涉及到屬性的getter方法,可以使用互斥鎖(@synchronized)可以保證屬性在多個(gè)線(xiàn)程之間的讀寫(xiě)都是安全的.
無(wú)論是atomic還是@synchronized ,使用的代價(jià)都是高昂的.
建議:
多線(xiàn)程是并發(fā)執(zhí)行多個(gè)任務(wù)提高效率的,如果可能,應(yīng)該在線(xiàn)程中避免爭(zhēng)搶共享資源.
正是出于性能的考慮,UIKit中的絕大多數(shù)類(lèi)都不是線(xiàn)程安全的,因此,蘋(píng)果公司要求:更新UI相關(guān)的操作,應(yīng)該在主線(xiàn)程中執(zhí)行.
1> 開(kāi)啟后臺(tái)執(zhí)行任務(wù)的方法:
2> 在后臺(tái)線(xiàn)程中通知主線(xiàn)程執(zhí)行任務(wù)的方法:
3> 獲取線(xiàn)程信息
4> 線(xiàn)程休眠
特點(diǎn):
1> 使用簡(jiǎn)單,輕量級(jí);
2> 不能控制線(xiàn)程的數(shù)量以及執(zhí)行順序.
NSObject的多線(xiàn)程方法注意事項(xiàng):
1> NSObject的多線(xiàn)程方法使用的是NSThread的多線(xiàn)程技術(shù).
2> NSThread的多線(xiàn)程技術(shù)不會(huì)自動(dòng)使用@autoreleasepool.
在使用NSObject或NSThread的多線(xiàn)程技術(shù)時(shí),如果涉及到對(duì)象分配,需要手動(dòng)添加@autoreleasepool.
聯(lián)系客服