iOS 多線程NSThread,GCD,NSOperation

單例模式例子:

https://github.com/XiaoRuiZuo/Singleton

多線程:
多線程是為了同步完成多項任務(wù),不是為了提高運行效率,而是為了提高資源使用效率來提高系統(tǒng)的效率。線程是在同一時間需要完成多項任務(wù)的時候?qū)崿F(xiàn)的。

3.多線程

多線程(multithreading),是指從軟件或者硬件上實現(xiàn)多個線程并發(fā)執(zhí)行的技術(shù)。具有多線程能力的計算機因有硬件支持而能夠在同一時間執(zhí)行多于一個線程,進而提升整體處理性能。

原理:

同一時間,CPU只能處理1條線程,只有1條線程在工作(執(zhí)行)

多線程并發(fā)(同時)執(zhí)行,其實是CPU快速地在多條線程之間調(diào)度(切換)

如果CPU調(diào)度線程的時間足夠快,就造成了多線程并發(fā)執(zhí)行的假象

注意:多線程并發(fā),并不是cpu在同一時刻同時執(zhí)行多個任務(wù),只是CPU調(diào)度足夠快,造成的假象。

優(yōu)點:

能適當(dāng)提高程序的執(zhí)行效率

能適當(dāng)提高資源利用率(CPU、內(nèi)存利用率)

缺點:

1.開啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,主線程占用1M,子線程占用512KB),如果開啟大量的線程,會占用大量的內(nèi)存空間,降低程序的性能

2.線程越多,CPU在調(diào)度線程上的開銷就越大

二、iOS開發(fā)中的應(yīng)用

1.主線程

一個iOS程序運行后,默認(rèn)會開啟1條線程,稱為“主線程”或“UI線程”。

作用:

顯示\刷新UI界面

處理UI事件(比如點擊事件、滾動事件、拖拽事件等)

注意:

刷新UI必須放在主線程

別將比較耗時的操作放到主線程中

耗時操作會卡住主線程,嚴(yán)重影響UI的流暢度

2.實現(xiàn)方案

NSThread

一個NSThread對象控制執(zhí)行的線程。當(dāng)你想在自己的執(zhí)行線程的Objective-C的方法運行使用這個類。當(dāng)你需要執(zhí)行一個漫長的任務(wù)線程是特別有用的,但不希望它阻止應(yīng)用程序的其余部分的執(zhí)行。特別是,您可以使用線程來避免阻塞應(yīng)用程序,它處理的用戶界面和事件相關(guān)的操作的主線。線程也可以用來將一個大的工作分成幾個較小的作業(yè),這可能會導(dǎo)致在多核計算機性能的提高。

一、創(chuàng)建和啟動線程

// 1.創(chuàng)建線程NSThread*thread = [[NSThreadalloc] initWithTarget:selfselector:@selector(run) object:nil];// 2.啟動線程[thread start];// 線程一啟動,就會在線程thread中執(zhí)行self的run方法// 創(chuàng)建線程后自動啟動線程[NSThreaddetachNewThreadSelector:@selector(run) toTarget:selfwithObject:nil];// 隱式創(chuàng)建并啟動線程[selfperformSelectorInBackground:@selector(run) withObject:nil];上述2種創(chuàng)建線程方式的優(yōu)缺點- 優(yōu)點:簡單快捷- 缺點:無法對線程進行更詳細(xì)的設(shè)置

二、主線程相關(guān)用法

// 返回主線程+ (NSThread*)mainThread;// 是否為主線程(類方法)+ (BOOL)isMainThread;// 是否為主線程(對象方法)- (BOOL)isMainThread;

三、其他用法

// 線程通知NSDidBecomeSingleThreadedNotificationNSThreadWillExitNotificationNSWillBecomeMultiThreadedNotification// 獲得當(dāng)前線程NSThread*current = [NSThreadcurrentThread];// 線程的名字- (void)setName:(NSString*)n; - (NSString*)name;

線程的狀態(tài)

線程的狀態(tài)

// 進入就緒狀態(tài) -> 運行狀態(tài)。當(dāng)線程任務(wù)執(zhí)行完畢,自動進入死亡狀態(tài)- (void)start;// 阻塞(暫停)線程->進入阻塞狀態(tài)+ (void)sleepUntilDate:(NSDate*)date;+ (void)sleepForTimeInterval:(NSTimeInterval)ti;// 強制停止線程-> 進入死亡狀態(tài)+ (void)exit;注意:一旦線程停止(死亡)了,就不能再次開啟任務(wù)

多線程的安全隱患

一塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源。

當(dāng)多個線程訪問同一塊資源時,很容易引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全問題。

解決辦法:(互斥鎖)

// 注意:鎖定1份代碼只用1把鎖,用多把鎖是無效的@synchronized(鎖對象) {// 需要鎖定的代碼? }

優(yōu)點與缺點

能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題

需要消耗大量的CPU資源

atomic與nonatomic

OC在定義屬性時有nonatomic和atomic兩種選擇

@property(nonatomic,copy)NSString*name;@property(atomic,copy)NSString*name;

atomic:原子屬性,為setter方法加鎖(默認(rèn)就是atomic)

線程安全,需要消耗大量的資源

nonatomic:非原子屬性,不會為setter方法加鎖

非線程安全,適合內(nèi)存小的移動設(shè)備

開發(fā)建議

所有屬性都聲明為nonatomic

盡量避免多線程搶奪同一塊資源

盡量將加鎖、資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器端處理,減小移動客戶端的壓力

線程間通信

在1個進程中,線程往往不是孤立存在的,多個線程之間需要經(jīng)常進行通信

例如在子線程下載圖片,在主線程刷新UI顯示圖片。

線程間通信常用方法

// 1.在主線程上執(zhí)行操作- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;// 2.在指定線程上執(zhí)行操作- (void)performSelector:(SEL)aSelector onThread:(NSThread*)thr withObject:(id)arg waitUnti


GCD

全稱是Grand Central Dispatch,“偉大的中樞調(diào)度器”

GCD是蘋果公司為多核的并行運算提出的解決方案

純C語言,提供了非常多強大的函數(shù)

優(yōu)勢

GCD會自動利用更多的CPU內(nèi)核(比如雙核、四核)

GCD會自動管理線程的生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)

只需要告訴GCD想要執(zhí)行什么任務(wù),不需要編寫任何線程管理代碼

基本概念

任務(wù)和隊列

GCD中有2個核心概念

1.任務(wù):執(zhí)行什么操作

2.隊列:用來存放任務(wù)

// 1.定制任務(wù):確定想做的事情// 2.將任務(wù)添加到隊列中:GCD會自動將隊列中的任務(wù)取出,放到對應(yīng)的線程中執(zhí)行。Tips:任務(wù)的取出遵循隊列的FIFO原則:先進先出,后進后出

任務(wù)

一、執(zhí)行任務(wù)

-queue:隊列 - block:任務(wù)// 1.用同步的方式執(zhí)行任務(wù)dispatch_sync(dispatch_queue_tqueue,dispatch_block_tblock);// 2.用異步的方式執(zhí)行任務(wù)dispatch_async(dispatch_queue_tqueue,dispatch_block_tblock);// 3.GCD中還有個用來執(zhí)行任務(wù)的函數(shù)// 在前面的任務(wù)執(zhí)行結(jié)束后它才執(zhí)行,而且它后面的任務(wù)等它執(zhí)行完成之后才會執(zhí)行dispatch_barrier_async(dispatch_queue_tqueue,dispatch_block_tblock);

注意:

同步:只能在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力

異步:可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力

隊列

一、并發(fā)隊列(Concurrent Dispatch Queue)

可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))

并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效

// 1.使用dispatch_queue_create函數(shù)創(chuàng)建隊列dispatch_queue_tdispatch_queue_create(constchar*label,// 隊列名稱dispatch_queue_attr_tattr);// 隊列的類型// 2.創(chuàng)建并發(fā)隊列dispatch_queue_tqueue= dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);// 3.使用dispatch_get_global_queue函數(shù)獲得全局的并發(fā)隊列dispatch_queue_tdispatch_get_global_queue(dispatch_queue_priority_tpriority,unsignedlongflags);// dispatch_queue_priority_t priority(隊列的優(yōu)先級 )// unsigned long flags( 此參數(shù)暫時無用,用0即可 )// 4.獲得全局并發(fā)隊列dispatch_queue_tqueue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);// 5.全局并發(fā)隊列的優(yōu)先級#defineDISPATCH_QUEUE_PRIORITY_HIGH2// 高#defineDISPATCH_QUEUE_PRIORITY_DEFAULT0// 默認(rèn)(中)#defineDISPATCH_QUEUE_PRIORITY_LOW (-2)// 低#defineDISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN// 后臺

二、串行隊列(Serial Dispatch Queue)

讓任務(wù)一個接著一個地執(zhí)行(一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù))

// 1.使用dispatch_queue_create函數(shù)創(chuàng)建串行隊列// 創(chuàng)建串行隊列(隊列類型傳遞NULL或者DISPATCH_QUEUE_SERIAL)dispatch_queue_tqueue= dispatch_queue_create("queue",NULL);// 2.使用dispatch_get_main_queue()獲得主隊列dispatch_queue_tqueue= dispatch_get_main_queue();注意:主隊列是GCD自帶的一種特殊的串行隊列,放在主隊列中的任務(wù),都會放到主線程中執(zhí)行。

三、各種隊列的執(zhí)行效果

特別注意:使用sync函數(shù)往當(dāng)前串行隊列中添加任務(wù),會卡住當(dāng)前的串行隊列 (線程卡死)

新手易混淆

有4個術(shù)語比較容易混淆:同步、異步、并發(fā)、串行

1.同步和異步主要影響:能不能開啟新的線程

同步:只是在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力

異步:可以在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力

2.并發(fā)和串行主要影響:任務(wù)的執(zhí)行方式

并發(fā):多個任務(wù)并發(fā)(同時)執(zhí)行

串行:一個任務(wù)執(zhí)行完畢后,再執(zhí)行下一個任務(wù)

GCD運用

一、線程間通信

從子線程回到主線程dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{// 執(zhí)行耗時的異步操作...dispatch_async(dispatch_get_main_queue(), ^{// 回到主線程,執(zhí)行UI刷新操作});});

二、延時執(zhí)行

dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2.0* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

// 2秒后異步執(zhí)行這里的代碼...

});

三、一次性代碼

// 使用dispatch_once函數(shù)能保證某段代碼在程序運行過程中只被執(zhí)行1次staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{// 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)});

四、快速迭代

// 使用dispatch_apply函數(shù)能進行快速迭代遍歷dispatch_apply(10, dispatch_get_global_queue(0,0), ^(size_tindex){// 執(zhí)行10次代碼,index順序不確定});

五、隊列組

// 分別異步執(zhí)行2個耗時的操作、2個異步操作都執(zhí)行完畢后,再回到主線程執(zhí)行操作dispatch_group_tgroup=? dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{// 執(zhí)行1個耗時的異步操作});dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{// 執(zhí)行1個耗時的異步操作});dispatch_group_notify(group, dispatch_get_main_queue(), ^{// 等前面的異步操作都執(zhí)行完畢后,回到主線程...});

單例模式

作用:

可以保證在程序運行過程,一個類只有一個實例,而且該實例易于供外界訪問。從而方便地控制了實例個數(shù),并節(jié)約系統(tǒng)資源

使用場合

在整個應(yīng)用程序中,共享一份資源(這份資源只需要創(chuàng)建初始化1次)

實現(xiàn)過程:

1.重寫實現(xiàn):

創(chuàng)建一個需要單例模式的文件

// 1.在.m中保留一個全局的static的實例staticid_instance;// 2.重寫allocWithZone:方法,在這里創(chuàng)建唯一的實例(注意線程安全)+ (instancetype)allocWithZone:(struct_NSZone*)zone{staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{? ? ? ? _instance = [superallocWithZone:zone];? ? });return_instance;}// 3.提供1個類方法讓外界訪問唯一的實例+ (instancetype)sharedInstance{staticdispatch_once_tonceToken;dispatch_once(&onceToken, ^{? ? ? ? _instance = [[selfalloc] init];? ? });return_instance;}// 4.實現(xiàn)copyWithZone:方法- (id)copyWithZone:(struct_NSZone*)zone{return_instance;}

2.宏實現(xiàn):

// .h文件#define SingletonH(name) + (instancetype)shared##name;// .m文件#define SingletonM(name)\static id _instance;\\+ (instancetype)allocWithZone:(struct _NSZone *)zone\{\static dispatch_once_t onceToken;\dispatch_once(&onceToken, ^{\_instance =[super allocWithZone:zone];\});\return _instance;\}\\+ (instancetype)shared##name\{\static dispatch_once_t onceToken;\dispatch_once(&onceToken, ^{\_instance =[[self alloc]init];\});\return _instance;\}\\- (id)copyWithZone:(NSZone *)zone\{\return _instance;\}


NSOperation

NSOperation是蘋果封裝的一套多線程的東西,不像GCD是純C語言的,這個是OC的。但相比較之下GCD會更快一些,但本質(zhì)上NSOPeration是多GDC的封裝。

NSOperation相對于GCD:

NSOperation擁有更多的函數(shù)可用

NSOperationQueue中,可以建立各個NSOperation之間的依賴關(guān)系。

NSOperationQueue支持KVO??梢员O(jiān)測operation是否正在執(zhí)行(isExecuted)、是否結(jié)束(isFinished),是否取消(isCanceld)

GCD 只支持FIFO 的隊列,而NSOperationQueue可以調(diào)整隊列的執(zhí)行順序

NSOperation剖析

NSOperation是個抽象類,并不具備封裝操作的能力,必須使用它的子類

使用NSOperation子類的方式有3種

NSInvocationOperation

NSBlockOperation

自定義子類繼承NSOperation,實現(xiàn)內(nèi)部相應(yīng)的方法

NSOperationQueue

1.NSOperation可以調(diào)用start方法來執(zhí)行任務(wù),但默認(rèn)是同步執(zhí)行的

2.如果將NSOperation添加到NSOperationQueue(操作隊列)中,系統(tǒng)會自動異步執(zhí)行NSOperation中的操作

// 添加操作到NSOperationQueue中- (void)addOperation:(NSOperation*)op;- (void)addOperationWithBlock:(void(^)(void))block;

NSOperation的使用

配合使用NSOperation和NSOperationQueue就能實現(xiàn)多線程編程

具體步驟:

1.將需要執(zhí)行的操作封裝到一個NSOperation對象中

2.將NSOperation對象添加到NSOperationQueue中

3.系統(tǒng)會自動將NSOperationQueue中的NSOperation取出來放到一條新線程中執(zhí)行

NSInvocationOperation子類

// 1.創(chuàng)建NSInvocationOperation對象- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;// 2.調(diào)用start方法開始執(zhí)行操作- (void)start;// 一旦執(zhí)行操作,就會調(diào)用target的sel方法

注意:

默認(rèn)情況下,調(diào)用了start方法后并不會開一條新線程去執(zhí)行操作,而是在當(dāng)前線程同步執(zhí)行操作。

只有將NSOperation放到一個NSOperationQueue中,才會異步執(zhí)行操作

NSBlockOperation子類

// 1.創(chuàng)建NSBlockOperation對象+ (id)blockOperationWithBlock:(void(^)(void))block;// 2.通過addExecutionBlock:方法添加更多的操作- (void)addExecutionBlock:(void(^)(void))block;

注意:

只要NSBlockOperation封裝的操作數(shù) > 1,就會異步執(zhí)行操作

自定義NSOperation

// 1.創(chuàng)建對象繼承NSOperation,重寫- (void)main;// 在里面實現(xiàn)想執(zhí)行的任務(wù)

注意:

自己創(chuàng)建自動釋放池(因為如果是異步操作,無法訪問主線程的自動釋放池)

經(jīng)常通過

-(BOOL)isCancelled

方法檢測操作是否被取消,對取消做出響應(yīng)。

NSOperation方法

一、最大并發(fā)數(shù)

可以通過對最大并發(fā)數(shù)設(shè)置,控制程序中線程的數(shù)量

// 1.最大并發(fā)數(shù)的相關(guān)方法- (NSInteger)maxConcurrentOperationCount;- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;

二、取消、暫停、恢復(fù)

// 1.取消隊列的所有操作- (void)cancelAllOperations;// 2.取消單個操作- (void)cancel// 暫停- (void)setSuspended:(BOOL)b;// YES代表暫停隊列,NO代表恢復(fù)隊列// 恢復(fù)隊列- (BOOL)isSuspended;

三、依賴

NSOperation之間可以設(shè)置依賴來保證執(zhí)行順序

// 1.比如一定要讓操作A執(zhí)行完后,才能執(zhí)行操作B,可以這么寫[operationB addDependency:operationA];// 操作B依賴于操作A注意:可以在不同queue的NSOperation之間創(chuàng)建依賴關(guān)系

四、操作的監(jiān)聽

// 1.可以監(jiān)聽一個操作的執(zhí)行完畢- (void(^)(void))completionBlock;- (void)setCompletionBlock:(void(^)(void))block;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容