線程問題我會分成三篇文章來給大家做個詳細的講解 一、線程的概念 1.線程是進程的組成部分,一個進程可以擁有多個線程,一個線程必須有一個父線程; 2.線程擁有自己的堆棧、自己的程式計數器和自己的局部變數,但不再擁有系統資源,它與父線程中的其他線程共用該進程所擁有的全部資源 3.線程是獨立運行的,它並不
線程問題我會分成三篇文章來給大家做個詳細的講解
一、線程的概念
1.線程是進程的組成部分,一個進程可以擁有多個線程,一個線程必須有一個父線程;
2.線程擁有自己的堆棧、自己的程式計數器和自己的局部變數,但不再擁有系統資源,它與父線程中的其他線程共用該進程所擁有的全部資源
3.線程是獨立運行的,它並不知道進程中是否還有其他線程存在,線程的執行是搶占式的,也就是說當前的運行的線程在任何時候都可能被掛起,以便另外一個線程可以運行。
使用多線程編程的優點
1、進程間不能共用記憶體,但線程之間共用記憶體非常容易
2、系統創建進程需要重新為該進程分配資源,但是創建線程則代價小得多,因為它與父線程中的其他線程共用該進程所擁有的全部資源
3、處理非同步任務的主要手段,可防止UI界面假死
線程的創建方法:
1)創建線程並執行線程
+ (void)detachNewThreadSelector: toTarget: withObject:
2)創建一個線程並返回一個線程對象但是不會執行,需要手動調用start
- (instancetype)initWithTarget: selector: object:
二、線程的狀態
1、當程式創建了一個線程後,該程式就處於新建狀態,此時它與其他OC對象一樣,僅僅由系統為其分配了記憶體,並初始化了其成員變數的值,此時的線程對象沒有表現任何線程的動態特征,程式也不會執行線程的線程執行體
2、當線程對象調用了start方法之後,該線程處於就緒狀態,系統會為其調用方法調用棧和程式計數器,處於這種狀態的線程並沒有開始運行,他只是表示該線程可以運行了,至於線程何時開始運行,取決於系統的調度;倘若希望調用子線程的start方法之後子線程立即開始執行,可以在程式中加上
[NSThread sleepForTimeInterval:0.001]; //寫在哪個線程中代表哪個線程
讓當前運行的線程睡眠一毫秒,因為在這一毫秒內CPU不會空閑,他會去執行另一個處於就緒狀態的線程,這就可以讓子線程立即獲得執行
三、終止子線程
線程會以以下三種方式之一結束,結束之後就處於死亡狀態
1、線程執行體方法執行完成,線程正常結束
2、線程執行過程中出現了錯誤
3、直接調用NSThread類的exit方法來終止當前正在執行的線程
對象方法
isExecuting、isFinishing判斷線程當前是否處於執行狀態或者執行完成狀態
如果希望在主線程中終止子線程,NSThread並沒有提供方法來終止某個子線程,為了在子線程中終止子線程,可以向子線程發送一個信號(比如調用子線程的cancel方法),然後在子線程的線程執行體重進行判斷,如果子線程收到過終止信號,程式應該調用NSThread類的exit方法來終止當前正在執行的迴圈
四、線程睡眠
如果需要讓當前正在執行的線程暫停一段時間,併進入阻塞狀態,則可以通過調用NSThread類的sleepXXX類方法來完成
+ (void)sleepForTimeInterval:(NSTimeInterval)ti; //每隔多久執行一次
+ (void)sleepUntilDate:(NSDate *)date; //指定睡眠指導某個時間執行
1 - (void)viewDidLoad { 2 [super viewDidLoad]; 3 // NSThread 兩種創建線程的方式 4 #if 1 5 //1.該方法創建的線程需要手動調用啟動,才會去執行線程指定的方法 6 // 線程需要一個函數作為線程的入口函數,這個函數稱為線程的入口函數 7 // 線程主要用來做併發操作,例如執行耗時的代碼 8 NSThread *tread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething) object:nil]; 9 for (int i = 0; i < 100; i++) { 10 if (i == 20) { 11 //需要手動執行開始,才會調用doSomething的方法 12 [tread start]; 13 [NSThread sleepForTimeInterval:0.001]; 14 } 15 NSLog(@"%d",i); 16 } 17 //給線程取別名 18 //tread.name = @"something"; 19 20 #elif 0 21 //2.該方式創建的線程會自動執行線程的入口函數 22 [NSThread detachNewThreadSelector:@selector(doSomething) toTarget:self withObject:nil]; 23 #endif 24 } 25 -(void)doSomething 26 { 27 // 耗時代碼 28 29 // 獲取當前線程 30 //NSLog(@"%@",[NSThread currentThread]); 31 int index = 10; 32 33 while (index--) { 34 35 if (index == 5) { 36 //1⃣️cancel 取消線程,並不是真正意義上的取消線程,是給線程打上取消的標誌,等待取消 37 [[NSThread currentThread] cancel]; 38 } 39 40 //2⃣️.判斷線程是否有取消的標誌 41 if ([[NSThread currentThread] isCancelled]) { 42 //3⃣️.退出線程,線程銷毀,系統會自動回收資源 43 //[NSThread exit]; 44 //註意return與退出線程的區別 45 //return; 46 } 47 48 NSLog(@"%d",index); 49 //兩種睡眠方式 50 //1.睡眠 51 //[NSThread sleepForTimeInterval:1]; 52 //2.指定睡眠知道某個時間執行 53 //dateByAddingTimeInterval 追加的時間 54 // NSDate *curDate = [[NSDate date] dateByAddingTimeInterval:1]; 55 // [NSThread sleepUntilDate:curDate]; 56 } 57 }
【註意】iOS規定只能在UI線程(即主線程)中修改UI控制項的屬性,因為如果程式允許任意子線程訪問、修改UI控制項的屬性,這就需要對多個新線程的併發訪問進行同步控制;否則,多個線程將會破壞UI控制項內部狀態的完整性
實例:下載網路圖片(如果程式在UI線程中訪問網路數據,由於網路速度的不確定性,當網路傳輸速度比較慢時,UI線程就會被阻塞,從而導致應用失去響應,因此程式將通過網路下載圖片的操作放在多線程中完成)
1 #import "ViewController.h" 2 3 @interface ViewController () 4 @property (weak, nonatomic) IBOutlet UIImageView *imageV; 5 6 @end 7 8 @implementation ViewController 9 10 - (void)viewDidLoad { 11 [super viewDidLoad]; 12 13 } 14 - (IBAction)BtnAction:(id)sender { 15 //圖片地址 16 NSString *url = @"http://image.baidu.com/search/down?tn=download&ipn=dwnl&word=download&ie=utf8&fr=result&url=http%3A%2F%2Fh.hiphotos.baidu.com%2Fzhidao%2Fwh%253D450%252C600%2Fsign%3D3dc4538262d0f703e6e79dd83dca7d0b%2F7a899e510fb30f24f570e996c895d143ac4b03b8.jpg&thumburl=http%3A%2F%2Fimg4.imgtn.bdimg.com%2Fit%2Fu%3D2019970444%2C20888940%26fm%3D21%26gp%3D0.jpg"; 17 //創建線程 18 NSThread *downloadImage = [[NSThread alloc] initWithTarget:self selector:@selector(downLoadImage:) object:url]; 19 //啟動線程 20 [downloadImage start]; 21 22 23 } 24 -(void)downLoadImage:(NSString *)url 25 { 26 NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]]; 27 UIImage *image = [UIImage imageWithData:data]; 28 //返回主線程刷新UI 29 30 [self performSelectorOnMainThread:@selector(reloadUI:) withObject:image waitUntilDone:NO]; 31 } 32 -(void)reloadUI:(UIImage *)image 33 { 34 _imageV.image = image; 35 } 36 @end