你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS多線程篇:NSThread簡單介紹和使用

iOS多線程篇:NSThread簡單介紹和使用

編輯:IOS開發綜合

一、什麼是NSThread

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

二、NSThread方法介紹

1)動態創建

NSThread*newThread=[[NSThreadalloc]initWithTarget:self

selector:@selector(threadRun)object:nil];

動態方法返回一個新的thread對象,需要調用start方法來啟動線程

2)靜態創建

1[NSThreaddetachNewThreadSelector:@selector(threadRun)toTarget:selfwithObject:nil];

由於靜態方法沒有返回值,如果需要獲取新創建的thread,需要在selector中調用獲取當前線程的方法

3)線程開啟

1[newThreadstart];

4)線程暫停

[NSThreadsleepForTimeInterval:1.0]; (以暫停一秒為例) [NSThreadsleepUntilDate:[NSDatedateWithTimeIntervalSinceNow:1.0]];

NSThread的暫停會有阻塞當前線程的效果

5)線程取消

[newThreadcancel];

取消線程並不會馬上停止並退出線程,僅僅只作(線程是否需要退出)狀態記錄

6)線程停止

[NSThreadexit];

停止方法會立即終止除主線程以外所有線程(無論是否在執行任務)並退出,需要在掌控所有線程狀態的情況下調用此方法,

否則可能會導致內存問題。

7)獲取當前線程

[NSThreadcurrentThread];

8)獲取主線程

[NSThreadmainThread];

9)線程優先級設置

iOS 8以前使用

[NSThreadsetThreadPriority:1.0];

這個方法的優先級的數值設置讓人困惑,因為你不知道你應該設置多大的值是比較合適的,因此在iOS8之後,threadPriority添加了

一句注釋:To be deprecated; use qualityOfService below

意思就是iOS 8以後推薦使用qualityOfService屬性,通過量化的優先級枚舉值來設置

qualityOfService的枚舉值如下:

NSQualityOfServiceUserInteractive:最高優先級,用於用戶交互事件

NSQualityOfServiceUserInitiated:次高優先級,用於用戶需要馬上執行的事件

NSQualityOfServiceDefault:默認優先級,主線程和沒有設置優先級的線程都默認為這個優先級

NSQualityOfServiceUtility:普通優先級,用於普通任務

NSQualityOfServiceBackground:最低優先級,用於不重要的任務

比如給線程設置次高優先級:

[newThreadsetQualityOfService:NSQualityOfServiceUserInitiated];

三、線程間通信

常用的有三種:

1、指定當前線程執行操作

[selfperformSelector:@selector(threadRun)]; [selfperformSelector:@selector(threadRun)withObject:nil]; [selfperformSelector:@selector(threadRun)withObject:nilafterDelay:2.0];

2、(在其他線程中)指定主線程執行操作

[selfperformSelectorOnMainThread:@selector(threadRun)withObject:nil

waitUntilDone:YES];

注意:更新UI要在主線程中進行

3、(在主線程中)指定其他線程執行操作

[selfperformSelector:@selector(threadRun)onThread:newThread

withObject:nilwaitUntilDone:YES];

//這裡指定為某個線程 [selfperformSelectorInBackground:@selector(threadRun)withObject:nil];

//這裡指定為後台線程

四、線程同步

線程和其他線程可能會共享一些資源,當多個線程同時讀寫同一份共享資源的時候,可能會引起沖突。線程同步是指是指在一定的時間內只允許

某一個線程訪問某個資源

iOS實現線程加鎖有NSLock和@synchronized兩種方式。

五、線程的創建和使用實例:模擬售票

情景:某演唱會門票發售,在廣州和北京均開設窗口進行銷售,以下是代碼實現

先監聽線程退出的通知,以便知道線程什麼時候退出

[[NSNotificationCenterdefaultCenter]addObserver:selfselector:@selector(threadExitNotice)

name:NSThreadWillExitNotificationobject:nil];

設置演唱會的門票數量

_ticketCount=50;

新建兩個子線程(代表兩個窗口同時銷售門票)

NSThread*window1=[[NSThreadalloc]initWithTarget:self

selector:@selector(saleTicket)object:nil]; window1.name=@"北京售票窗口"; [window1start]; NSThread*window2=[[NSThreadalloc]initWithTarget:self

selector:@selector(saleTicket)object:nil]; window2.name=@"廣州售票窗口"; [window2start]; 線程啟動後,執行saleTicket,執行完畢後就會退出,為了模擬持續售票的過程,

我們需要給它加一個循環 -(void)saleTicket{ while(1){ //如果還有票,繼續售賣 if(_ticketCount>0){ _ticketCount--; NSLog(@"%@",[NSStringstringWithFormat:@"剩余票數:%ld窗口:%@",_ticketCount,[NSThreadcurrentThread].name]); [NSThreadsleepForTimeInterval:0.2]; } //如果已賣完,關閉售票窗口 else{ break; } } }

執行結果:

2016-04-0619:25:36.637MutiThread[4705:1371666]剩余票數:9窗口:廣州售票窗口 2016-04-0619:25:36.637MutiThread[4705:1371665]剩余票數:8窗口:北京售票窗口 2016-04-0619:25:36.839MutiThread[4705:1371666]剩余票數:7窗口:廣州售票窗口 2016-04-0619:25:36.839MutiThread[4705:1371665]剩余票數:7窗口:北京售票窗口 2016-04-0619:25:37.045MutiThread[4705:1371666]剩余票數:5窗口:廣州售票窗口 2016-04-0619:25:37.045MutiThread[4705:1371665]剩余票數:6窗口:北京售票窗口 2016-04-0619:25:37.250MutiThread[4705:1371665]剩余票數:4窗口:北京售票窗口 2016-04-0619:25:37.250MutiThread[4705:1371666]剩余票數:4窗口:廣州售票窗口 2016-04-0619:25:37.456MutiThread[4705:1371666]剩余票數:2窗口:廣州售票窗口 2016-04-0619:25:37.456MutiThread[4705:1371665]剩余票數:3窗口:北京售票窗口 2016-04-0619:25:37.661MutiThread[4705:1371665]剩余票數:1窗口:北京售票窗口 2016-04-0619:25:37.661MutiThread[4705:1371666]剩余票數:1窗口:廣州售票窗口 2016-04-0619:25:37.866MutiThread[4705:1371665]剩余票數:0窗口:北京售票窗口 2016-04-0619:25:37.867MutiThread[4705:1371666]

{number=3,name=廣州售票窗口}WillExit 2016-04-0619:25:38.070MutiThread[4705:1371665]

{number=2,name=北京售票窗口}WillExit

可以看到,票的銷售過程中出現了剩余數量錯亂的情況,這就是前面提到的線程同步問題。

售票是一個典型的需要線程同步的場景,由於售票渠道有很多,而票的資源是有限的,當多個渠道在短時間內賣出大量

的票的時候,如果沒有同步機制來管理票的數量,將會導致票的總數和售出票數對應不上的錯誤。

我們在售票的過程中給票加上同步鎖:同一時間內,只有一個線程能對票的數量進行操作,當操作完成之後,其他線程

才能繼續對票的數量進行操作。

-(void)saleTicket{ while(1){ @synchronized(self){ //如果還有票,繼續售賣 if(_ticketCount>0){ _ticketCount--; NSLog(@"%@",[NSStringstringWithFormat:@"剩余票數:%ld窗口:%@",_ticketCount,[NSThreadcurrentThread].name]); [NSThreadsleepForTimeInterval:0.2]; } //如果已賣完,關閉售票窗口 else{ break; } } } }

運行結果:

2016-04-0619:31:27.913MutiThread[4718:1406865]剩余票數:11窗口:北京售票窗口 2016-04-0619:31:28.115MutiThread[4718:1406866]剩余票數:10窗口:廣州售票窗口 2016-04-0619:31:28.317MutiThread[4718:1406865]剩余票數:9窗口:北京售票窗口 2016-04-0619:31:28.522MutiThread[4718:1406866]剩余票數:8窗口:廣州售票窗口 2016-04-0619:31:28.728MutiThread[4718:1406865]剩余票數:7窗口:北京售票窗口 2016-04-0619:31:28.929MutiThread[4718:1406866]剩余票數:6窗口:廣州售票窗口 2016-04-0619:31:29.134MutiThread[4718:1406865]剩余票數:5窗口:北京售票窗口 2016-04-0619:31:29.339MutiThread[4718:1406866]剩余票數:4窗口:廣州售票窗口 2016-04-0619:31:29.545MutiThread[4718:1406865]剩余票數:3窗口:北京售票窗口 2016-04-0619:31:29.751MutiThread[4718:1406866]剩余票數:2窗口:廣州售票窗口 2016-04-0619:31:29.952MutiThread[4718:1406865]剩余票數:1窗口:北京售票窗口 2016-04-0619:31:30.158MutiThread[4718:1406866]剩余票數:0窗口:廣州售票窗口 2016-04-0619:31:30.363MutiThread[4718:1406866]

{number=3,name=廣州售票窗口}WillExit 2016-04-0619:31:30.363MutiThread[4718:1406865]

{number=2,name=北京售票窗口}WillExit

可以看到,票的數量沒有出現錯亂的情況。

線程的持續運行和退出

我們注意到,線程啟動後,執行saleTicket完畢後就馬上退出了,怎樣能讓線程一直運行呢(窗口一直開放,

可以隨時指派其賣演唱會的門票的任務),答案就是給線程加上runLoop

//先監聽線程退出的通知,以便知道線程什麼時候退出 [[NSNotificationCenterdefaultCenter]addObserver:self

selector:@selector(threadExitNotice)

name:NSThreadWillExitNotificationobject:nil];

1 2//設置演唱會的門票數量 _ticketCount=50;

新建兩個子線程(代表兩個窗口同時銷售門票)

NSThread*window1=[[NSThreadalloc]initWithTarget:self

selector:@selector(thread1)object:nil]; [window1start]; NSThread*window2=[[NSThreadalloc]initWithTarget:self

selector:@selector(thread2)object:nil]; [window2start];

接著我們給線程創建一個runLoop

-(void)thread1{ [NSThreadcurrentThread].name=@"北京售票窗口"; NSRunLoop*runLoop1=[NSRunLoopcurrentRunLoop]; [runLoop1runUntilDate:[NSDatedate]];//一直運行 } -(void)thread2{ [NSThreadcurrentThread].name=@"廣州售票窗口"; NSRunLoop*runLoop2=[NSRunLoopcurrentRunLoop]; [runLoop2runMode:NSDefaultRunLoopModebeforeDate:[NSDatedateWithTimeIntervalSinceNow:10.0]];//自定義運行時間 }

然後就可以指派任務給線程了,這裡我們讓兩個線程都執行相同的任務(售票)

[selfperformSelector:@selector(saleTicket)onThread:window1

withObject:nilwaitUntilDone:NO]; [selfperformSelector:@selector(saleTicket)onThread:window2

withObject:nilwaitUntilDone:NO];

運行結果:

2016-04-0619:43:22.585MutiThread[4762:1478200]剩余票數:11窗口:北京售票窗口 2016-04-0619:43:22.788MutiThread[4762:1478201]剩余票數:10窗口:廣州售票窗口 2016-04-0619:43:22.993MutiThread[4762:1478200]剩余票數:9窗口:北京售票窗口 2016-04-0619:43:23.198MutiThread[4762:1478201]剩余票數:8窗口:廣州售票窗口 2016-04-0619:43:23.404MutiThread[4762:1478200]剩余票數:7窗口:北京售票窗口 2016-04-0619:43:23.609MutiThread[4762:1478201]剩余票數:6窗口:廣州售票窗口 2016-04-0619:43:23.810MutiThread[4762:1478200]剩余票數:5窗口:北京售票窗口 2016-04-0619:43:24.011MutiThread[4762:1478201]剩余票數:4窗口:廣州售票窗口 2016-04-0619:43:24.216MutiThread[4762:1478200]剩余票數:3窗口:北京售票窗口 2016-04-0619:43:24.422MutiThread[4762:1478201]剩余票數:2窗口:廣州售票窗口 2016-04-0619:43:24.628MutiThread[4762:1478200]剩余票數:1窗口:北京售票窗口 2016-04-0619:43:24.833MutiThread[4762:1478201]剩余票數:0窗口:廣州售票窗口 2016-04-0619:43:25.039MutiThread[4762:1478201]

{number=3,name=廣州售票窗口}

WillExit

可以看到,當票賣完後,兩個線程並沒有退出,仍在繼續運行,當到達指定時間後,線程2退出了,

如果需要讓線程1退出,需要我們手動管理。

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

-(void)saleTicket{ while(1){ @synchronized(self){ //如果還有票,繼續售賣 if(_ticketCount>0){ _ticketCount--; NSLog(@"%@",[NSStringstringWithFormat:@"剩余票數:%ld窗口:%@",_ticketCount,[NSThreadcurrentThread].name]); [NSThreadsleepForTimeInterval:0.2]; } //如果已賣完,關閉售票窗口 else{ if([NSThreadcurrentThread].isCancelled){ break; }else{ NSLog(@"售賣完畢"); //給當前線程標記為取消狀態 [[NSThreadcurrentThread]cancel]; //停止當前線程的runLoop CFRunLoopStop(CFRunLoopGetCurrent()); } } } } }

運行結果:

2016-04-0620:08:38.287MutiThread[4927:1577193]剩余票數:10窗口:北京售票窗口 2016-04-0620:08:38.489MutiThread[4927:1577194]剩余票數:9窗口:廣州售票窗口 2016-04-0620:08:38.690MutiThread[4927:1577193]剩余票數:8窗口:北京售票窗口 2016-04-0620:08:38.892MutiThread[4927:1577194]剩余票數:7窗口:廣州售票窗口 2016-04-0620:08:39.094MutiThread[4927:1577193]剩余票數:6窗口:北京售票窗口 2016-04-0620:08:39.294MutiThread[4927:1577194]剩余票數:5窗口:廣州售票窗口 2016-04-0620:08:39.499MutiThread[4927:1577193]剩余票數:4窗口:北京售票窗口 2016-04-0620:08:39.700MutiThread[4927:1577194]剩余票數:3窗口:廣州售票窗口 2016-04-0620:08:39.905MutiThread[4927:1577193]剩余票數:2窗口:北京售票窗口 2016-04-0620:08:40.106MutiThread[4927:1577194]剩余票數:1窗口:廣州售票窗口 2016-04-0620:08:40.312MutiThread[4927:1577193]剩余票數:0窗口:北京售票窗口 2016-04-0620:08:40.516MutiThread[4927:1577194]售賣完畢 2016-04-0620:08:40.516MutiThread[4927:1577193]售賣完畢 2016-04-0620:08:40.517MutiThread[4927:1577193]

{number=2,name=北京售票窗口}WillExit 2016-04-0620:08:40.517MutiThread[4927:1577194]

{number=3,name=廣州售票窗口}

WillExit

如果確定兩個線程都是isCancelled狀態,可以調用[NSThread exit]方法來終止線程。

    NSThread

    一個NSThread對象就代表一條線程NSThread會在執行完任務函數是被自動收回一些常用的函數
        //創建線程
        NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector() object:nil];
        //object存的是參數
    
        //修改參數名
        thread.name = @"我的多線程";
    
        //獲取主線程
        NSThread *thread = [NSThread mainThread];
    
        //創建線程並自動啟動
        [NSThread detachNewThreadSelector:@selector() toTarget:self withObject:nil];
    
        //隱士創建
        [self performSelectorInBackground:@selector() withObject:nil];
    
        //獲取當前線程
        [NSThread currentThread]
    
        //啟動線程
        - (void)start;
    
    
        //阻塞(暫停)線程
        + (void)sleepUntilDate:(NSDate *)date;
        + (void)sleepForTimeInterval:(NSTimeInterval)ti;
        // 進入阻塞狀態
    
        //停止當前正在運行的進程
        + (void)exit;
        // 進入死亡狀態,一旦死亡則不能重啟

    資源共享

    1塊資源可能會被多個線程共享,也就是多個線程可能會訪問同一塊資源當多個線程訪問同一塊資源時,很容易引發數據錯亂和數據安全問題

    解決辦法互斥鎖

    互斥鎖使用格式

    @synchronized(鎖對象) { // 需要鎖定的代碼 }只用一把鎖,多鎖是無效的

    互斥鎖的優缺點

    優點:能有效防止因多線程搶奪資源造成的數據安全問題缺點:需要消耗大量的CPU資源

    互斥鎖的使用前提:多條線程搶奪同一塊資源

    互斥鎖的示例代碼
    #import "ViewController.h"
    
    @interface ViewController ()
    /** 售票機1*/
    @property (strong,nonatomic) NSThread *threadOne;
    /** 售票機2*/
    @property (strong,nonatomic) NSThread *threadTwo;
    /** 售票機3*/
    @property (strong,nonatomic) NSThread *threadThree;
    /** 售票機4*/
    @property (strong,nonatomic) NSThread *threadFour;
    /** 售票機5*/
    @property (strong,nonatomic) NSThread *threadFive;
    
    /** 數量*/
    @property (assign,nonatomic) NSInteger count;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //創建線程
        self.threadOne = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"one"];
        self.threadTwo = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"two"];
        self.threadThree = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"three"];
        self.threadFour = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"four"];
        self.threadFive = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:@"five"];
        self.count = 100;
    }
    
    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
    {
        //開始線程
        [self.threadOne start];
        [self.threadTwo start];
        [self.threadThree start];
        [self.threadFour start];
        [self.threadFive start];
    }
    
    
    - (void)run:(NSString *)param
    {
        while (1) {
            //self是鎖對象
            @synchronized(self)
            {
    
                if (self.count > 0) {
                    _count--;
                    NSLog(@"%zd-----%@",self.count,[NSThread currentThread]);
                }
                else
                {
                    NSLog(@"賣完了----%@",[NSThread currentThread]);
                    break;
                }
            }
        }
    }
    @end
    
    
    1. 上一頁:
    2. 下一頁:
    蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
    Copyright © Ios教程網 All Rights Reserved