iOS多線程篇:NSThread

一懊直、什么是NSThread

NSThread是基于線程使用,輕量級的多線程編程方法(相對GCD和NSOperation)火鼻,一個NSThread對象代表一個線程室囊,需要手動管理線程的生命周期,處理線程同步等問題魁索。

二融撞、NSThread方法介紹

1)動態(tài)創(chuàng)建
    NSThread * newThread = [[NSThread alloc]initWithTarget:self selector:@selector(threadRun) object:nil];

動態(tài)方法返回一個新的thread對象,需要調(diào)用start方法來啟動線程

2)靜態(tài)創(chuàng)建
    [NSThread detachNewThreadSelector:@selector(threadRun) toTarget:self withObject:nil];

由于靜態(tài)方法沒有返回值粗蔚,如果需要獲取新創(chuàng)建的thread尝偎,需要在selector中調(diào)用獲取當(dāng)前線程的方法

3)線程開啟
    [newThread start];
    ```
######4)線程暫停
[NSThread sleepForTimeInterval:1.0]; (以暫停一秒為例)
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];
  NSThread的暫停會有阻塞當(dāng)前線程的效果
######5)線程取消
[newThread cancel];
  取消線程并不會馬上停止并退出線程鹏控,僅僅只作(線程是否需要退出)狀態(tài)記錄
    
######6)線程停止
[NSThread exit];
  停止方法會立即終止除主線程以外所有線程(無論是否在執(zhí)行任務(wù))并退出致扯,需要在掌控所有線程狀態(tài)的情況下調(diào)用此方法,否則可能會導(dǎo)致內(nèi)存問題当辐。
    
######7)獲取當(dāng)前線程
[NSThread currentThread];
    
######8)獲取主線程
[NSThread mainThread];
    
######9)線程優(yōu)先級設(shè)置
  iOS8以前使用
[NSThread setThreadPriority:1.0];
  這個方法的優(yōu)先級的數(shù)值設(shè)置讓人困惑抖僵,因為你不知道你應(yīng)該設(shè)置多大的值是比較合適的,因此在iOS8之后缘揪,threadPriority添加了一句注釋:``To be deprecated; use qualityOfService below``

  意思就是iOS8以后推薦使用qualityOfService屬性耍群,通過量化的優(yōu)先級枚舉值來設(shè)置
  qualityOfService的枚舉值如下:
    NSQualityOfServiceUserInteractive:最高優(yōu)先級,用于用戶交互事件
    NSQualityOfServiceUserInitiated:次高優(yōu)先級找筝,用于用戶需要馬上執(zhí)行的事件
    NSQualityOfServiceDefault:默認優(yōu)先級蹈垢,主線程和沒有設(shè)置優(yōu)先級的線程都默認為這個優(yōu)先級
    NSQualityOfServiceUtility:普通優(yōu)先級,用于普通任務(wù)
    NSQualityOfServiceBackground:最低優(yōu)先級袖裕,用于不重要的任務(wù)

  比如給線程設(shè)置次高優(yōu)先級:
[newThread setQualityOfService:NSQualityOfServiceUserInitiated];
    
###三耘婚、線程間通信
  常用的有三種:
######  1、指定當(dāng)前線程執(zhí)行操作
[self performSelector:@selector(threadRun)];
[self performSelector:@selector(threadRun) withObject:nil];
[self performSelector:@selector(threadRun) withObject:nil afterDelay:2.0];
######  2陆赋、(在其他線程中)指定主線程執(zhí)行操作
[self performSelectorOnMainThread:@selector(threadRun) withObject:nil waitUntilDone:YES];
  注意:更新UI要在主線程中進行
    
######  3沐祷、(在主線程中)指定其他線程執(zhí)行操作
[self performSelector:@selector(threadRun) onThread:newThread withObject:nil waitUntilDone:YES]; //這里指定為某個線程
[self performSelectorInBackground:@selector(threadRun) withObject:nil];//這里指定為后臺線程
    
###四、線程同步
  線程和其他線程可能會共享一些資源攒岛,當(dāng)多個線程同時讀寫同一份共享資源的時候赖临,可能會引起沖突。線程同步是指是指在一定的時間內(nèi)只允許某一個線程訪問某個資源

  iOS實現(xiàn)線程加鎖有NSLock和@synchronized兩種方式
    
###五灾锯、線程的創(chuàng)建和使用實例:模擬售票
  情景:某演唱會門票發(fā)售兢榨,在廣州和北京均開設(shè)窗口進行銷售,以下是代碼實現(xiàn)
 先監(jiān)聽線程退出的通知,以便知道線程什么時候退出
 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(threadExitNotice) name:NSThreadWillExitNotification object:nil];
 ```
     設(shè)置演唱會的門票數(shù)量
     _ticketCount = 50;
     ```
 新建兩個子線程(代表兩個窗口同時銷售門票)
 NSThread * window1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
 window1.name = @"北京售票窗口";
 [window1 start];
 
 NSThread * window2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil];
 window2.name = @"廣州售票窗口";
 [window2 start];
 ```
     線程啟動后吵聪,執(zhí)行saleTicket凌那,執(zhí)行完畢后就會退出,為了模擬持續(xù)售票的過程吟逝,我們需要給它加一個循環(huán)
     - (void)saleTicket {
         while (1) {
             //如果還有票帽蝶,繼續(xù)售賣
             if (_ticketCount > 0) {
                 _ticketCount --;
                 NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%ld 窗口:%@", _ticketCount, [NSThread currentThread].name]);
                 [NSThread sleepForTimeInterval:0.2];
             }
             //如果已賣完,關(guān)閉售票窗口
             else {
                 break;
             }
         }
     }
     ```
 執(zhí)行結(jié)果:
2016-04-06 19:25:36.637 MutiThread[4705:1371666] 剩余票數(shù):9 窗口:廣州售票窗口
2016-04-06 19:25:36.637 MutiThread[4705:1371665] 剩余票數(shù):8 窗口:北京售票窗口
2016-04-06 19:25:36.839 MutiThread[4705:1371666] 剩余票數(shù):7 窗口:廣州售票窗口
2016-04-06 19:25:36.839 MutiThread[4705:1371665] 剩余票數(shù):7 窗口:北京售票窗口
2016-04-06 19:25:37.045 MutiThread[4705:1371666] 剩余票數(shù):5 窗口:廣州售票窗口
2016-04-06 19:25:37.045 MutiThread[4705:1371665] 剩余票數(shù):6 窗口:北京售票窗口
2016-04-06 19:25:37.250 MutiThread[4705:1371665] 剩余票數(shù):4 窗口:北京售票窗口
2016-04-06 19:25:37.250 MutiThread[4705:1371666] 剩余票數(shù):4 窗口:廣州售票窗口
2016-04-06 19:25:37.456 MutiThread[4705:1371666] 剩余票數(shù):2 窗口:廣州售票窗口
2016-04-06 19:25:37.456 MutiThread[4705:1371665] 剩余票數(shù):3 窗口:北京售票窗口
2016-04-06 19:25:37.661 MutiThread[4705:1371665] 剩余票數(shù):1 窗口:北京售票窗口
2016-04-06 19:25:37.661 MutiThread[4705:1371666] 剩余票數(shù):1 窗口:廣州售票窗口
2016-04-06 19:25:37.866 MutiThread[4705:1371665] 剩余票數(shù):0 窗口:北京售票窗口
2016-04-06 19:25:37.867 MutiThread[4705:1371666] <NSThread: 0x7fdc91e289f0>{number = 3, name = 廣州售票窗口} Will Exit
2016-04-06 19:25:38.070 MutiThread[4705:1371665] <NSThread: 0x7fdc91e24d60>{number = 2, name = 北京售票窗口} Will Exit
  可以看到块攒,票的銷售過程中出現(xiàn)了剩余數(shù)量錯亂的情況励稳,這就是前面提到的線程同步問題。
    
  售票是一個典型的需要線程同步的場景囱井,由于售票渠道有很多驹尼,而票的資源是有限的瓶珊,當(dāng)多個渠道在短時間內(nèi)賣出大量的票的時候轰绵,如果沒有同步機制來管理票的數(shù)量煞躬,將會導(dǎo)致票的總數(shù)和售出票數(shù)對應(yīng)不上的錯誤幕屹。
 我們在售票的過程中給票加上同步鎖:同一時間內(nèi)胸遇,只有一個線程能對票的數(shù)量進行操作交洗,當(dāng)操作完成之后改衩,其他線程才能繼續(xù)對票的數(shù)量進行操作权均。
 - (void)saleTicket {
     while (1) {
         @synchronized(self) {
             //如果還有票澎羞,繼續(xù)售賣
             if (_ticketCount > 0) {
                 _ticketCount --;
                 NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%ld 窗口:%@", _ticketCount, [NSThread currentThread].name]);
                 [NSThread sleepForTimeInterval:0.2];
             }
             //如果已賣完髓绽,關(guān)閉售票窗口
             else {
                 break;
             }
         }
     }
 }
 運行結(jié)果:
 2016-04-06 19:31:27.913 MutiThread[4718:1406865] 剩余票數(shù):11 窗口:北京售票窗口
 2016-04-06 19:31:28.115 MutiThread[4718:1406866] 剩余票數(shù):10 窗口:廣州售票窗口
 2016-04-06 19:31:28.317 MutiThread[4718:1406865] 剩余票數(shù):9 窗口:北京售票窗口
 2016-04-06 19:31:28.522 MutiThread[4718:1406866] 剩余票數(shù):8 窗口:廣州售票窗口
 2016-04-06 19:31:28.728 MutiThread[4718:1406865] 剩余票數(shù):7 窗口:北京售票窗口
 2016-04-06 19:31:28.929 MutiThread[4718:1406866] 剩余票數(shù):6 窗口:廣州售票窗口
 2016-04-06 19:31:29.134 MutiThread[4718:1406865] 剩余票數(shù):5 窗口:北京售票窗口
 2016-04-06 19:31:29.339 MutiThread[4718:1406866] 剩余票數(shù):4 窗口:廣州售票窗口
 2016-04-06 19:31:29.545 MutiThread[4718:1406865] 剩余票數(shù):3 窗口:北京售票窗口
 2016-04-06 19:31:29.751 MutiThread[4718:1406866] 剩余票數(shù):2 窗口:廣州售票窗口
 2016-04-06 19:31:29.952 MutiThread[4718:1406865] 剩余票數(shù):1 窗口:北京售票窗口
 2016-04-06 19:31:30.158 MutiThread[4718:1406866] 剩余票數(shù):0 窗口:廣州售票窗口
 2016-04-06 19:31:30.363 MutiThread[4718:1406866] <NSThread: 0x7ff0c1637320>{number = 3, name = 廣州售票窗口} Will Exit
 2016-04-06 19:31:30.363 MutiThread[4718:1406865] <NSThread: 0x7ff0c1420cb0>{number = 2, name = 北京售票窗口} Will Exit
 ```

可以看到,票的數(shù)量沒有出現(xiàn)錯亂的情況妆绞。

線程的持續(xù)運行和退出

我們注意到顺呕,線程啟動后,執(zhí)行saleTicket完畢后就馬上退出了括饶,怎樣能讓線程一直運行呢(窗口一直開放株茶,可以隨時指派其賣演唱會的門票的任務(wù)),答案就是給線程加上runLoop
先監(jiān)聽線程退出的通知图焰,以便知道線程什么時候退出 [[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(threadExitNotice) name:NSThreadWillExitNotification object:nil];

     //設(shè)置演唱會的門票數(shù)量
     _ticketCount = 50;
     ```
 新建兩個子線程(代表兩個窗口同時銷售門票)
 NSThread * window1 = [[NSThread alloc]initWithTarget:self selector:@selector(thread1) object:nil];
 [window1 start];
 
 NSThread * window2 = [[NSThread alloc]initWithTarget:self selector:@selector(thread2) object:nil];
 [window2 start];
 ```
     接著我們給線程創(chuàng)建一個runLoop
     - (void)thread1 {
         [NSThread currentThread].name = @"北京售票窗口";
         NSRunLoop * runLoop1 = [NSRunLoop currentRunLoop];
         [runLoop1 runUntilDate:[NSDate date]]; //一直運行
     }
     
     - (void)thread2 {
         [NSThread currentThread].name = @"廣州售票窗口";
         NSRunLoop * runLoop2 = [NSRunLoop currentRunLoop];
         [runLoop2 runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10.0]]; //自定義運行時間
     }
     ```
 然后就可以指派任務(wù)給線程了启盛,這里我們讓兩個線程都執(zhí)行相同的任務(wù)(售票)
 [self performSelector:@selector(saleTicket) onThread:window1 withObject:nil waitUntilDone:NO];
 [self performSelector:@selector(saleTicket) onThread:window2 withObject:nil waitUntilDone:NO];
 ```
     運行結(jié)果:
     2016-04-06 19:43:22.585 MutiThread[4762:1478200] 剩余票數(shù):11 窗口:北京售票窗口
     2016-04-06 19:43:22.788 MutiThread[4762:1478201] 剩余票數(shù):10 窗口:廣州售票窗口
     2016-04-06 19:43:22.993 MutiThread[4762:1478200] 剩余票數(shù):9 窗口:北京售票窗口
     2016-04-06 19:43:23.198 MutiThread[4762:1478201] 剩余票數(shù):8 窗口:廣州售票窗口
     2016-04-06 19:43:23.404 MutiThread[4762:1478200] 剩余票數(shù):7 窗口:北京售票窗口
     2016-04-06 19:43:23.609 MutiThread[4762:1478201] 剩余票數(shù):6 窗口:廣州售票窗口
     2016-04-06 19:43:23.810 MutiThread[4762:1478200] 剩余票數(shù):5 窗口:北京售票窗口
     2016-04-06 19:43:24.011 MutiThread[4762:1478201] 剩余票數(shù):4 窗口:廣州售票窗口
     2016-04-06 19:43:24.216 MutiThread[4762:1478200] 剩余票數(shù):3 窗口:北京售票窗口
     2016-04-06 19:43:24.422 MutiThread[4762:1478201] 剩余票數(shù):2 窗口:廣州售票窗口
     2016-04-06 19:43:24.628 MutiThread[4762:1478200] 剩余票數(shù):1 窗口:北京售票窗口
     2016-04-06 19:43:24.833 MutiThread[4762:1478201] 剩余票數(shù):0 窗口:廣州售票窗口
     2016-04-06 19:43:25.039 MutiThread[4762:1478201] <NSThread: 0x7fe0d3c24360>{number = 3, name = 廣州售票窗口} Will Exit

可以看到,當(dāng)票賣完后技羔,兩個線程并沒有退出僵闯,仍在繼續(xù)運行,當(dāng)?shù)竭_指定時間后藤滥,線程2退出了鳖粟,如果需要讓線程1退出,需要我們手動管理拙绊。

比如我們讓線程完成任務(wù)(售票)后自行退出向图,可以這樣操作

     - (void)saleTicket {
         while (1) {
             @synchronized(self) {
             //如果還有票泳秀,繼續(xù)售賣
                 if (_ticketCount > 0) {
                     _ticketCount --;
                     NSLog(@"%@", [NSString stringWithFormat:@"剩余票數(shù):%ld 窗口:%@", _ticketCount, [NSThread currentThread].name]);
                     [NSThread sleepForTimeInterval:0.2];
                 }
                 //如果已賣完,關(guān)閉售票窗口
                 else {
                     if ([NSThread currentThread].isCancelled) {
                        break;
                     }else {
                        NSLog(@"售賣完畢");
                        //給當(dāng)前線程標記為取消狀態(tài)
                        [[NSThread currentThread] cancel];
                        //停止當(dāng)前線程的runLoop
                        CFRunLoopStop(CFRunLoopGetCurrent());
                     }
                 }
             }
         }
     }
     ```
 運行結(jié)果:
 2016-04-06 20:08:38.287 MutiThread[4927:1577193] 剩余票數(shù):10 窗口:北京售票窗口
 2016-04-06 20:08:38.489 MutiThread[4927:1577194] 剩余票數(shù):9 窗口:廣州售票窗口
 2016-04-06 20:08:38.690 MutiThread[4927:1577193] 剩余票數(shù):8 窗口:北京售票窗口
 2016-04-06 20:08:38.892 MutiThread[4927:1577194] 剩余票數(shù):7 窗口:廣州售票窗口
 2016-04-06 20:08:39.094 MutiThread[4927:1577193] 剩余票數(shù):6 窗口:北京售票窗口
 2016-04-06 20:08:39.294 MutiThread[4927:1577194] 剩余票數(shù):5 窗口:廣州售票窗口
 2016-04-06 20:08:39.499 MutiThread[4927:1577193] 剩余票數(shù):4 窗口:北京售票窗口
 2016-04-06 20:08:39.700 MutiThread[4927:1577194] 剩余票數(shù):3 窗口:廣州售票窗口
 2016-04-06 20:08:39.905 MutiThread[4927:1577193] 剩余票數(shù):2 窗口:北京售票窗口
 2016-04-06 20:08:40.106 MutiThread[4927:1577194] 剩余票數(shù):1 窗口:廣州售票窗口
 2016-04-06 20:08:40.312 MutiThread[4927:1577193] 剩余票數(shù):0 窗口:北京售票窗口
 2016-04-06 20:08:40.516 MutiThread[4927:1577194] 售賣完畢
 2016-04-06 20:08:40.516 MutiThread[4927:1577193] 售賣完畢
 2016-04-06 20:08:40.517 MutiThread[4927:1577193] <NSThread: 0x7fb719d54000>{number = 2, name = 北京售票窗口} Will Exit
 2016-04-06 20:08:40.517 MutiThread[4927:1577194] <NSThread: 0x7fb719d552f0>{number = 3, name = 廣州售票窗口} Will Exit
  如果確定兩個線程都是isCancelled狀態(tài)榄攀,可以調(diào)用[NSThread exit]方法來終止線程嗜傅。
     
###Next
  接下來將更新GCD和NSOperation篇

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市檩赢,隨后出現(xiàn)的幾起案子吕嘀,更是在濱河造成了極大的恐慌,老刑警劉巖漠畜,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件币他,死亡現(xiàn)場離奇詭異坞靶,居然都是意外死亡憔狞,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門彰阴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瘾敢,“玉大人,你說我怎么就攤上這事尿这〈氐郑” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵射众,是天一觀的道長碟摆。 經(jīng)常有香客問我,道長叨橱,這世上最難降的妖魔是什么典蜕? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮罗洗,結(jié)果婚禮上愉舔,老公的妹妹穿的比我還像新娘。我一直安慰自己伙菜,他們只是感情好轩缤,可當(dāng)我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贩绕,像睡著了一般火的。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上淑倾,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天馏鹤,我揣著相機與錄音,去河邊找鬼踊淳。 笑死假瞬,一個胖子當(dāng)著我的面吹牛陕靠,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脱茉,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼剪芥,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了琴许?” 一聲冷哼從身側(cè)響起税肪,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎榜田,沒想到半個月后益兄,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡箭券,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年净捅,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辩块。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡蛔六,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出废亭,到底是詐尸還是另有隱情国章,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布豆村,位于F島的核電站液兽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏掌动。R本人自食惡果不足惜四啰,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坏匪。 院中可真熱鬧拟逮,春花似錦、人聲如沸适滓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凭迹。三九已至罚屋,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嗅绸,已是汗流浹背脾猛。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鱼鸠,地道東北人猛拴。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓羹铅,卻偏偏與公主長得像,于是被迫代替她去往敵國和親愉昆。 傳聞我的和親對象是個殘疾皇子职员,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容