ios 多線程的故事2

上文我們簡單的敘述了多線程,那么這篇我們就詳細(xì)的說一下!

多線程技術(shù)方案

多線程技術(shù)方案

PThread

導(dǎo)入頭文件

#import <pthread.h>

創(chuàng)建線程

/**

參數(shù)

1. 指向線程標(biāo)識符的指針,C 語言中類型的結(jié)尾通常 _t/Ref,而且不需要使用 *

2. 線程屬性

3. 線程調(diào)用的函數(shù)

void * 返回類型

(*) 函數(shù)指針

void * 參數(shù)類型

4. 運(yùn)行函數(shù)的參數(shù)

返回值

- 若線程創(chuàng)建成功,則返回 0

- 若線程創(chuàng)建失敗,則返回出錯編號

*/

pthread_t threadId =NULL;

char * name ="zhangsan";

NSString * ocName =@"lisi";

int age =19;

// 1> 傳遞 C 語言字符串

//? ? int result = pthread_create(&threadId, NULL, demo, name);

// 2> 傳遞 OC 字符串

//? ? int result = pthread_create(&threadId, NULL, demo, (__bridge void *)(ocName));

// 3> 傳遞基本數(shù)據(jù)類型intresult = pthread_create(&threadId,NULL, demo, &age);

if(result ==0) {

NSLog(@"成功");? ?

}else{

NSLog(@"失敗");? ?

}

線程調(diào)用函數(shù)

/// 線程調(diào)用函數(shù)

void * demo(void* param) {

//? ? NSString *name = (__bridge NSString *)(param);

//? ? NSLog(@"%@ %@", [NSThread currentThread], name);

int * age = param;

NSLog(@"%@ %d", [NSThreadcurrentThread], *age);

return ?NULL;

}

小結(jié)

1. ?在 C 語言中,沒有對象的概念,對象是以結(jié)構(gòu)體的方式來實(shí)現(xiàn)的

2. ?通常,在 C 語言框架中,對象類型以_t/Ref結(jié)尾,而且聲明時不需要使用*

3. ?C 語言中的void *和 OC 中的id是類似的

4. ?內(nèi)存管理

1) ?在 OC 中,如果是ARC開發(fā),編譯器會在編譯時,根據(jù)代碼結(jié)構(gòu),自動添加retain/release/autorelease

2) ?但是,ARC 只負(fù)責(zé)管理 OC 部分的內(nèi)存管理,而不負(fù)責(zé) C 語言 代碼的內(nèi)存管理

3) ?因此,開發(fā)過程中,如果使用的 C 語言框架出現(xiàn)retain/create/copy/new等字樣的函數(shù),大多都需要release,否則會出現(xiàn)內(nèi)存泄漏

4. ?在混合開發(fā)時,如果在 C 和 OC 之間傳遞數(shù)據(jù),需要使用__bridge進(jìn)行橋接,橋接的目的就是為了告訴編譯器如何管理內(nèi)存

1) ?橋接的添加可以借助 Xcode 的輔助功能添加

2) ?MRC 中不需要使用橋接

NSThread

創(chuàng)建線程的三種方式

準(zhǔn)備線程執(zhí)行方法

#pragma mark - 線程執(zhí)行方法

- (void)longOperation:(id)param {

NSLog(@"%@ - %@", [NSThread currentThread], param);

}

alloc/init創(chuàng)建線程

#pragma mark - 創(chuàng)建線程

/// 使用 alloc / init 創(chuàng)建線程

- (void)thread1 {

NSLog(@"before %@", [NSThread currentThread]);

NSThread*thread = [[NSThread alloc] initWithTarget:selfselector:@selector(longOperation:) object:@"hello"];?

? [thread start];

NSLog(@"after %@", [NSThread currentThread]);

}

代碼小結(jié)

1. ?[thread start];執(zhí)行后,會在另外一個線程執(zhí)行l(wèi)ongOperation:方法

2. ?在 OC 中,任何一個方法的代碼都是從上向下順序執(zhí)行的

3. ?同一個方法內(nèi)的代碼,都是在相同線程執(zhí)行的(block除外)

使用類方法創(chuàng)建線程

/// 使用 NSThread 類方法

- (void)thread2 {? ? [NSThreaddetachNewThreadSelector:@selector(longOperation:) toTarget:self withObject:@(__FUNCTION__)];}

注: detachNewThreadSelector類方法不需要啟動,會自動創(chuàng)建線程并執(zhí)行@selector方法

使用 NSObject 分類方法創(chuàng)建線程

/// 使用 NSObject 分類方法

- (void)thread3 {

? ?[self performSelectorInBackground:@selector(longOperation:) ? withObject:@(__FUNCTION__)];

}

代碼小結(jié)

1. ?performSelectorInBackground是NSObject的分類方法

2. ?會自動在后臺線程執(zhí)行@selector方法

3. ?沒有thread字眼,隱式創(chuàng)建并啟動線程

4. ?所有NSObject都可以使用此方法,在其他線程執(zhí)行方法

5. ?Swift中不支持

NSThread 的 Target

NSThread的實(shí)例化方法中的target指的是開啟線程后,在線程中執(zhí)行哪一個對象的@selector方法

準(zhǔn)備對象

@interface Person : NSObject

- (void)run;

@end

@implementation Person

- (void)run {

NSLog(@"跑了 5 分鐘 %@", [NSThread currentThread]);

}

@end

異步執(zhí)行對象方法

// 1. 創(chuàng)建 Person 對象

Person *person = [Person new];

// 2. 在異步執(zhí)行對象方法

// 1> 方式 1

NSThread * t = [[NSThread alloc] initWithTarget:person selector:@selector(run) object:nil];? ?

[t start];

// 2> 方式2

[NSThread detachNewThreadSelector:@selector(run) toTarget:person withObject:nil];

// 3> 方式3

[person performSelectorInBackground:@selector(run) withObject:nil];

代碼小結(jié)

1. ?通過指定不同的target會在后臺線程執(zhí)行該對象的@selector方法

2. ?提示:不要看見target就寫self

3. ?performSelectorInBackground可以讓方便地在后臺線程執(zhí)行任意NSObject對象的方法

線程的狀態(tài)

線程的狀態(tài)

狀態(tài)說明

新建

1. ?實(shí)例化線程對象

就緒

1. ?向線程對象發(fā)送start消息,線程對象被加入可調(diào)度線程池等待 CPU 調(diào)度

2. ?detach方法和performSelectorInBackground方法會直接實(shí)例化一個線程對象并加入可調(diào)度線程池

運(yùn)行

1. ?CPU負(fù)責(zé)調(diào)度可調(diào)度線程池中線程的執(zhí)行

2. ?線程執(zhí)行完成之前,狀態(tài)可能會在就緒和運(yùn)行之間來回切換

3. ?就緒和運(yùn)行之間的狀態(tài)變化由 CPU 負(fù)責(zé),程序員不能干預(yù)

阻塞

1. ?當(dāng)滿足某個預(yù)定條件時,可以使用休眠或鎖阻塞線程執(zhí)行

2. ?sleepForTimeInterval:休眠指定時長

3. ?sleepUntilDate:休眠到指定日期

4. ?@synchronized(self):互斥鎖

死亡

1. ?正常死亡

2. ?線程執(zhí)行完畢

3. ?非正常死亡

4. ?當(dāng)滿足某個條件后,在線程內(nèi)部中止執(zhí)行

5. ?當(dāng)滿足某個條件后,在主線程中止線程對象

控制線程狀態(tài)的方法

啟動

1. ?[_thread start]

? ? 1) ? 線程進(jìn)入就緒狀態(tài),當(dāng)線程執(zhí)行完畢后自動進(jìn)入死亡狀態(tài)

休眠

1. ?方法執(zhí)行過程中,符合某一條件時,可以利用sleep方法讓線程進(jìn)入阻塞狀態(tài)

? ? 1) ?sleepForTimeInterval從現(xiàn)在起睡多少秒

? ?2) ?sleepUntilDate從現(xiàn)在起睡到指定的日期

死亡

1. ?[NSThread exit]

? ? 1) ?一旦強(qiáng)行終止線程,后續(xù)的所有代碼都不會被執(zhí)行

? ?2) ?注意:在終止線程之前,應(yīng)該注意釋放之前分配的對象!

取消

1. ?[_thread cancel]

1) ?并不會直接取消線程

2) ?只是給線程對象添加isCancelled標(biāo)記

3) ?需要在線程內(nèi)部的關(guān)鍵代碼位置,增加判斷,決定是否取消當(dāng)前線程

代碼演練

定義成員變量

@implementation ViewController{

NSThread*_thread;

}

實(shí)現(xiàn)touch方法

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event {

// 跟蹤線程狀態(tài)

NSLog(@"%@ %zd %zd %zd", _thread, _thread.isFinished, _thread.isCancelled, _thread.isExecuting);

if(_thread ==nil|| _thread.isFinished|| _thread.isCancelled) {

// 1. 實(shí)例化線程

_thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];

// 2. 啟動線程,添加到可調(diào)度線程池

[_thread start];

? ? }

}

實(shí)現(xiàn)調(diào)度方法,測試阻塞線程執(zhí)行

- (void)demo {NSLog(@"開始");// 1. 阻塞到指定日期[NSThreadsleepUntilDate:[NSDatedateWithTimeIntervalSinceNow:1]];for(NSIntegeri =0; i <10; i++) {NSLog(@"%zd %@", i, [NSThreadcurrentThread]);// 1> 阻塞指定時長[NSThreadsleepForTimeInterval:1];? ? }NSLog(@"over");}

在線程內(nèi)部直接終止線程

- (void)demo {

NSLog(@"開始");

// 1. 阻塞到指定日期

[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];

for(NSInteger i =0; i <10; i++) {

NSLog(@"%zd %@", i, [NSThread currentThread]);

// 1> 阻塞指定時長

[NSThread sleepForTimeInterval:1];

// 2> 當(dāng)滿足某一個條件后,直接終止線程,線程終止后,不會執(zhí)行后續(xù)代碼

if(i ==5) {? ? ?

? ? ? [NSThread exit];? ?

? ? }? ?

}

NSLog(@"over");

}

在外部終止線程

修改touch方法

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event {

// 跟蹤線程狀態(tài)

NSLog(@"%@ %zd %zd %zd", _thread, _thread.isFinished, _thread.isCancelled, _thread.isExecuting);

if(_thread ==nil|| _thread.isFinished|| _thread.isCancelled) {

// 1. 實(shí)例化線程_thread = [[NSThreadalloc] initWithTarget:selfselector:@selector(demo) object:nil];

// 2. 啟動線程,添加到可調(diào)度線程池

[_thread start];?

? }else{

// 線程執(zhí)行中,直接終止

[_thread cancel];?

? }

}

運(yùn)行測試,線程并不會被終止

給線程對象發(fā)送cancel消息,只是給線程對象增加的一個標(biāo)記

是否終止需要在線程調(diào)用的代碼內(nèi)部處理

修改demo函數(shù)

- (void)demo {

NSLog(@"開始");

// 1. 阻塞到指定日期

[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];

if([NSThread currentThread].isCancelled) {

NSLog(@"被取消");

return;? ?

}

for(NSInteger i =0; i <10; i++) {

NSLog(@"%zd %@", i, [NSThread currentThread]);

if([NSThread currentThread].isCancelled) {

NSLog(@"被取消");

return;

? ? ? ? }

// 1> 阻塞指定時長

[NSThread sleepForTimeInterval:1];

// 2> 當(dāng)滿足某一個條件后,直接終止線程,線程終止后,不會執(zhí)行后續(xù)代碼

if(i ==5) {

? ? ? ? ? ? [NSThreadexit];

? ? ? }

? ? }

NSLog(@"over");

}

線程的屬性

常用屬性

1. ?name- 線程名稱

1). 設(shè)置線程名稱可以當(dāng)線程執(zhí)行的方法內(nèi)部出現(xiàn)異常時,記錄異常和當(dāng)前線程

2. ?stackSize- 棧區(qū)大小

1). 默認(rèn)情況下,無論是主線程還是子線程,棧區(qū)大小都是512K

2). 棧區(qū)大小可以設(shè)置[NSThread currentThread].stackSize = 1024 * 1024;

3). 必須是4KB的倍數(shù)

3. ?isMainThread- 是否主線程

4. ?threadPriority- 線程優(yōu)先級

1). 優(yōu)先級,是一個浮點(diǎn)數(shù),取值范圍從0~1.0

2). 1.0表示優(yōu)先級最高

3). 0.0表示優(yōu)先級最低

4). 默認(rèn)優(yōu)先級是0.5

5). 優(yōu)先級高只是保證 CPU 調(diào)度的可能性會高

5. ?qualityOfService- 服務(wù)質(zhì)量(iOS 8.0 推出)

1). NSQualityOfServiceUserInitiated- 用戶需要

2). NSQualityOfServiceUtility- 實(shí)用工具,用戶不需要立即得到結(jié)果

3). NSQualityOfServiceBackground- 后臺

4). NSQualityOfServiceDefault- 默認(rèn),介于用戶需要和實(shí)用工具之間

5). NSQualityOfServiceUserInteractive- 用戶交互,例如繪圖或者處理用戶事件

關(guān)于優(yōu)先級和服務(wù)質(zhì)量

1. ?多線程的目的:是將耗時的操作放在后臺,不阻塞主線程和用戶的交互!

2. ?多線程開發(fā)的原則:簡單

3. ?在開發(fā)時,最好不要修改優(yōu)先級,不要相信用戶交互服務(wù)質(zhì)量

4. ?內(nèi)核調(diào)度算法在決定該運(yùn)行哪個線程時,會把線程的優(yōu)先級作為考量因素

1). 較高優(yōu)先級的線程會比較低優(yōu)先級的線程具有更多的運(yùn)行機(jī)會

2).較高優(yōu)先級不保證你的線程具體執(zhí)行的時間,只是相比較低優(yōu)先級的線程,更有可能被調(diào)度器選擇執(zhí)行而已

代碼

- (void)viewDidLoad {?

? [superview DidLoad];

if([NSThread currentThread].isMainThread){

NSLog(@"主線程 %zd K", [NSThread currentThread].stackSize/1024);?

? }

NSThread * t1 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];

// 指定線程名稱,便于調(diào)試

t1.name=@"thread 001";

// 指定線程優(yōu)先級,CPU 會增加改線程的調(diào)度頻率

//? ? t1.threadPriority = 1;

// 指定線程服務(wù)質(zhì)量

t1.qualityOfService=NSQualityOfServiceUserInitiated;

? ? [t1 start];

NSThread * t2 = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];

? ? t2.name=@"thread 002";

? ? t2.threadPriority=0;

? ? t2.qualityOfService=NSQualityOfServiceBackground;

? ? [t2 start];

}

- (void)demo {

for(NSInteger i =0; i <10; i++) {

NSLog(@"%@ %zd K", [NSThread currentThread], [NSThread currentThread].stackSize/1024);

? ? }

// 模擬線程崩潰

if(![NSThread currentThread].isMainThread) {

int i =10;

int x =0;

NSLog(@"%zd", i / x);

? ? }

}

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

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

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