iOS 多線程之NSThread簡單使用 – iPhone手機開發技術文章 iPhone軟體開發教學課程

1、線程的構建和開啟:

(1)_thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(threadOneMethod) object:nil];
    _thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(threadTwoMethod) object:nil];
    
    [_thread1 start];
    [_thread2 start];

先構建,然後開啟,好處是可以控制開啟的時間,並且可以得到NSThread對象,便於之後和這個線程通訊。

  (2) [NSThread detachNewThreadSelector:@selector(threadOneMethod) toTarget:self withObject:nil];
  (3) [self performSelectorInBackground:@selector(threadOneMethod) withObject:nil];

還有一種是從系統的NSThread繼承一個子類,這個子類的構建和父類一樣,但是線程內執行的代碼是寫在-(void
)main方法裡面的:

@interface MyThread : NSThread //從NSThread繼承

@end

////////////////////
-(void )main{
    for (int i = 0; i<10; i++) {
        NSLog(@"myThread count: %d",i);
        sleep(1);
    }
    NSLog(@"thread end");
}

// 使用自定義的線程對象
_thread3 = [[MyThread alloc]init];
    [_thread3 start];

相對而言,前面3個方法構建的線程,線程內執行的方法都是在當前的類文件裡面的,而使用main函數,線程中執行的方法是屬於線程對象本身的,這樣可以在任何其他需要使用這個線程方法的地方使用,而不用在實現一次方法。

2、線程同步,即線程鎖的使用:

(1)@synchronized關鍵詞:

-(void )threadOneMethod{
    @synchronized(@"lock"){  
        for (int i= 0; i<10; i++) { //循環10次,每次輸出一個標識,然後線程沉睡一秒
            NSLog(@"111");
            sleep(1); 
        }
    }
    NSLog(@"thread1 end");
}
///////////////////////

-(void )threadTwoMethod{
    @synchronized(@"lock"){
        for (int i= 0; i<5; i++) { //循環5次,每次輸出一個標識222,然後線程沉睡1秒
            NSLog(@"222");
            sleep(1);
        }
    }
    NSLog(@"thread2 end");
}

上面的例子是結合第一種線程構建方法的,兩個線程幾乎同時開啟,但是第一個更快一些,執行後效果是,第一個方法循環輸出10次“111”結束後,第二個方法才輸出“222”。因為第一個方法開始進入@synchronized(@”lock”)裡面之後,第二個方法在執行到@synchronized(@”lock”)這裡就進不去瞭,會等到前面一個線程從裡面出來,它才能繼續向下進行,這就是線程鎖的作用。有些時候多個線程會同時操作用同一個數據或對象,可能會出現意想不到的糟糕情況,會使用線程鎖讓某個線程在操作的時候,其他線程無法操作,從而保證線程安全。

@synchronized(@”lock”)是使用括號裡面的對象來區分的,如果把第二個方法裡的對象改掉,變成@synchronized(@”111111″),就不會起到鎖的作用瞭,因為這是它們就是兩個完全不相關的鎖瞭。

2、NSLock:

[_lock lock];
        for (int i= 0; i<10; i++) {
            NSLog(@"111");
            sleep(1);
        }
 [_lock unlock];

核心的方法部分沒有區別,隻是把鎖換成瞭[_lock lock]……[_lock unlock];這裡的鎖是依靠_lock這個NSLock對象來識別的,就是說到某個NSLock對象調用瞭lock方法後,其他線程使用同一個NSLock對象調用lock方法就會停滯不前進,知道同一個NSLock對象再調用unlock方法。

這個過程有點像有些體檢項目,房間裡同時隻能體檢一個人,一個人進去後就會把門鎖上,這時有其他人來瞭也隻能等著,等到裡面的人好瞭出來,門打開,下一個才能進去。

3、線程間通訊:

副線程給主線程發消息:

[self performSelectorOnMainThread:@selector(mainThreadMethod) withObject:nil waitUntilDone:NO];

self是方法的調用者,mainThreadMethod是讓主線程調用的方法,然後這個方法可以帶參數,也就是object,最後的waitUntilDone是指是否等到主線程把方法執行完瞭,這個performSelector方法才返回。

向其他任意線程發消息:

[self performSelector:@selector(timerFire) onThread:_thread1 withObject:nil waitUntilDone:NO];

主要是這裡要傳入一個NSThread對象用於指定在哪個線程執行,這也是前面構建線程的時候保留線程對象的意義。

然後,需要註意的是,這些performSelector形式的方法,都需要對方線程的RunLoop處於開啟狀態,因為這些方法實質是runloop的輸入源,把消息發送給對方線程的runloop,然後對方從runloop裡面獲取消息,才去執行方法。主線程的runloop是默認開啟的,副線程的runloop是默認構建,但是需要手動開啟。

4、線程的關閉:

需要關閉某個線程,可以給這個線程發消息,使他關閉:

- (IBAction)killThread:(UIButton *)sender {

    [self performSelector:@selector(exitThread:) onThread:_thread1 withObject:_thread1 waitUntilDone:NO];
}
////////////////

-(void)exitThread:(NSThread *)thread{
    NSLog(@"thread EXIT1");
    [NSThread exit];
    NSLog(@"thread EXIT2");
}

也可以:

-(void )threadOneMethod{
    //前面寫線程需要執行任務的代碼,最後進入runloop循環,保持線程不結束同時保持接受消息
    
    NSRunLoop *theRL = [NSRunLoop currentRunLoop];
    while (shouldKeepRunning  ){//通過shouldKeepRunning判斷是否繼續進行循環,如果為NO,就會停止循環,然後繼續向下運行,線程自然結束
        NSLog(@"looprun");
        [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
    };

    NSLog(@"thread1 end");
}
///////////

BOOL shouldKeepRunning = YES;//一個全局的BOOL類型變量
- (IBAction)killThread:(UIButton *)sender {//點擊按鈕,修改shouldKeepRunning為NO
    shouldKeepRunning = NO;
    
}

這裡是通過一個全局變量的改變來控制線程的繼續還是結束。但是有個小問題是,當線程的runloop接受瞭外來的輸入源之後,例如其他線程調用:

[self performSelector:@selector(timerFire) onThread:_thread1 withObject:nil waitUntilDone:NO];

在這個線程運行,runloop接受到消息後會阻塞在方法[theRL
runMode:NSDefaultRunLoopMode
beforeDate:[NSDate
distantFuture]]裡面,也即是說while循環不會繼續向下一個循環進行,那麼改變shouldKeepRunning就不能馬上得到反饋,所以需要使用:

BOOL shouldKeepRunning = YES;
- (IBAction)killThread:(UIButton *)sender {
    [self performSelector:@selector(exitThread:) onThread:_thread1 withObject:nil waitUntilDone:NO];
    
}
/////////結合
-(void)exitThread:(NSThread *)thread{
    shouldKeepRunning = NO;
}

這樣就是給要關閉的線程發消息,會立刻喚醒目標線程的runloop,因為[theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]方法的特性是在接觸到輸入源後方法立刻返回,這樣while循環就會立刻進入進入下一個循環,也就會進行循環條件的判斷,然後因為shouldKeepRunning變為NO瞭,就會退出循環,然後線程結束。

兩種線程結束的方法,貌似第二種更自然些,具體對於線程內部的運行和內存的管理上的影響,還得研究下。

發佈留言