NSThread-詳解


當(dāng)你想在線程中執(zhí)行一個(gè)很長的任務(wù),但又不希望它阻塞應(yīng)用程序其余部分的執(zhí)行時(shí),子線程尤其有用,你可以將任務(wù)放在子線程中來執(zhí)行,避免阻塞應(yīng)用程序的主線程,讓主線程處理用戶界面和與事件相關(guān)的操作,子線程用于將大型任務(wù)劃分為幾個(gè)較小的任務(wù),這能會(huì)使多核計(jì)算機(jī)上的性能提高。


NSThread使用

  • 獲取主線程&當(dāng)前線程
      // 獲取主線程
      NSThread *mainThred = [NSThread mainThread];
      // 獲取當(dāng)前線程
      NSThread *currentThred = [NSThread currentThread];
    
  • 線程的創(chuàng)建
  1. 這種創(chuàng)建方式需要手動(dòng)啟動(dòng)線程
- (void)viewDidLoad {
    [super viewDidLoad];
    // 創(chuàng)建一條線程(方式一)
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(runThread:) object:@"我是一條線程"];
    // 啟動(dòng)線程
    [thread start];
}
  1. 以下幾種方式創(chuàng)建線程會(huì)自動(dòng)啟動(dòng)線程,但是出了方式二以外的幾種創(chuàng)建方式都無法對(duì)線程進(jìn)行詳細(xì)的設(shè)置。
// 方式二
   NSThread *thread = [[NSThread alloc] initWithBlock:^{
       
   }];
// 方式三
   [NSThread detachNewThreadWithBlock:^{
          NSLog(@"thread=%@-------",[NSThread currentThread]);
   }];
// 方式四
   [NSThread detachNewThreadSelector:@selector(runThread:) toTarget:self withObject:@"線程"];
// 方式五
   [self  performSelectorInBackground:@selector(runThread:) withObject:@"線程"];

  • 常用屬性
// 優(yōu)先級(jí) 0.0 ~ 1.0 默認(rèn) 0.5 數(shù)值越大優(yōu)先級(jí)越高
@property double threadPriority ; 

/** NSQualityOfService:
  NSQualityOfServiceUserInteractive:最高優(yōu)先級(jí),主要用于提供交互UI的操作,比如處理點(diǎn)擊事件,繪制圖像到屏幕上
  NSQualityOfServiceUserInitiated:次高優(yōu)先級(jí),主要用于執(zhí)行需要立即返回的任務(wù)
  NSQualityOfServiceDefault:默認(rèn)優(yōu)先級(jí),當(dāng)沒有設(shè)置優(yōu)先級(jí)的時(shí)候,線程默認(rèn)優(yōu)先級(jí)
  NSQualityOfServiceUtility:普通優(yōu)先級(jí),主要用于不需要立即返回的任務(wù)
  NSQualityOfServiceBackground:后臺(tái)優(yōu)先級(jí),用于完全不緊急的任務(wù)
*/
@property NSQualityOfService qualityOfService; 

// 線程名稱(區(qū)別多條線程)
@property (nullable, copy) NSString *name;

// 線程正在執(zhí)行
@property (readonly, getter=isExecuting) BOOL executing;

// 線程執(zhí)行結(jié)束
@property (readonly, getter=isFinished) BOOL finished;

// 線程是否可以取消
@property (readonly, getter=isCancelled) BOOL cancelled;
  • 常用方法
-(void)start; // 啟動(dòng)線程

-(BOOL)isMainThread; // 是否為主線程

-(void)setName:(NSString *)n; // 設(shè)置線程名稱

-(void)cancel ;// 取消線程

-(void)isExecuting; // 判斷線程是否正在執(zhí)行

-(void)isCancelled; // 判斷線程是否撤銷

-(void)isFinished; // 判斷線程是否已經(jīng)結(jié)束

+(void)currentThread; // 獲取當(dāng)前線程

+(BOOL)isMultiThreaded;  // 當(dāng)前代碼運(yùn)行所在線程是否是子線程

+(void)sleepUntilDate:(NSDate *)date;  //當(dāng)前代碼所在線程睡到指定時(shí)間

+(void)sleepForTimeInterval:(NSTimeInterval)ti; //當(dāng)前線程睡多長
時(shí)間

+(void)exit; // 退出當(dāng)前線程
  • 線程的生命周期

當(dāng)線程中的任務(wù)執(zhí)行完成過后被釋放

  • 線程的狀態(tài)
    1. 就緒狀態(tài):當(dāng)前線程準(zhǔn)備就緒,CPU調(diào)度當(dāng)前線程--->變?yōu)檫\(yùn)行狀態(tài)。
    2. 運(yùn)行狀態(tài):當(dāng)前線程運(yùn)行中,當(dāng)CPU調(diào)度其他線程--->變?yōu)榫途w狀態(tài)。
    3. 阻塞狀態(tài):當(dāng)前線程調(diào)用sleep方法,等睡眠時(shí)間結(jié)束--->變?yōu)榫途w狀態(tài)。
    4. 死亡狀態(tài):線程任務(wù)執(zhí)行完成,移出可調(diào)度線程池,被釋放掉。

線程的安全

  • 如果多個(gè)線程(同時(shí))訪問同一資源時(shí),很容易引發(fā)數(shù)據(jù)錯(cuò)亂和數(shù)據(jù)安全問題。


    線程安全.png

總共有100張電影票,當(dāng)售票員01和02同時(shí)獲取到余票為一百張時(shí),01賣出了80張余票為20張,02賣出了21張余票為79張,實(shí)際上他們賣出了101張但是總票數(shù)只有100張,這樣就多出一個(gè)人沒法看電影,這就是引發(fā)數(shù)據(jù)錯(cuò)亂。


#import "ViewController.h"

@interface ViewController ()
@property (nonatomic,strong) NSThread *threadA;
@property (nonatomic,strong) NSThread *threadB;
@property (nonatomic,strong) NSThread *threadC;

@property (nonatomic,assign) NSInteger totalCount;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 總共設(shè)置100 票
    self.totalCount = 100;
    
    self.threadA = [[NSThread alloc] initWithTarget:self selector:@selector(runThread:) object:nil];
    self.threadB = [[NSThread alloc] initWithTarget:self selector:@selector(runThread:) object:nil];
    self.threadC = [[NSThread alloc] initWithTarget:self selector:@selector(runThread:) object:nil];
    
    self.threadA.name = @"售票員A";
    self.threadB.name = @"售票員B";
    self.threadC.name = @"售票員C";
    
    [self.threadA start];
    [self.threadB start];
    [self.threadC start];

}

-(void)runThread:(id)obj{
    while (1) {
        NSInteger count = self.totalCount;
        if (count>0) {
            for (int i = 0; i<1000; i++) { // 模擬售票時(shí)間
                self.totalCount = count - 1;
            }
            NSLog(@"%@賣了一張票還剩%zd",[NSThread currentThread].name,self.totalCount);
        }else{
            NSLog(@"售完了");
            break;
        }
    }
}
@end
輸出結(jié)果.png
  • 互斥鎖 「@synchronized () { }」
    • 鎖是必須全局唯一的
    • 注意加鎖的位置。
    • 注意加鎖條件(多線程訪問同一資源)
    • 加鎖的結(jié)果會(huì)導(dǎo)致線程同步(多條線程在同一條線上執(zhí)行,按順序執(zhí)行任務(wù))

-(void)runThread:(id)obj{
 
    while (1) {
        // 鎖:必須是全局唯一的
        @synchronized (self) {  // 互斥鎖
            NSInteger count = self.totalCount;
            if (count>0) {
                for (int i = 0; i<1000; i++) { // 模擬售票時(shí)間
                    self.totalCount = count - 1;
                }
                NSLog(@"%@賣了一張票還剩%zd",[NSThread currentThread].name,self.totalCount);
            }else{
                NSLog(@"售完了");
                break;
            }
        }
    }
      
}
互斥鎖結(jié)果.png

非原子屬&性原子

  • nonatomic:非原子屬性,不會(huì)為setter方法加鎖,非線程安全,適合內(nèi)存小的移動(dòng)設(shè)備。
  • atomic:原子屬性,為setter方法加鎖(默認(rèn)就是atomic),線程安全,需要耗費(fèi)大量的資源。

線程間通信

  • 一個(gè)線程傳遞數(shù)據(jù)給另一個(gè)線程
  • 在一個(gè)線程中執(zhí)行完成任務(wù),轉(zhuǎn)到另一個(gè)線程中繼續(xù)執(zhí)行任務(wù)(列如子線程下載圖片,主線程顯示圖片)
  • 常用方法
// 從當(dāng)前線程切換到主線程中
-(void)performSelectorOnMainThread:(SEL)aSelector
                        withObject:(id)arg
                     waitUntilDone:(BOOL)wait;
    
// 從當(dāng)前線程切換到指定的線程
-(void)performSelector:(SEL)aSelector
              onThread:(NSThread *)thr
            withObject:(id)arg
         waitUntilDone:(BOOL)wait;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • NSThread簡(jiǎn)介 NSThread是蘋果官方提供面向?qū)ο蟛僮骶€程的技術(shù),簡(jiǎn)單方便,可以直接操作線程對(duì)象,不過需...
    一個(gè)默默無聞的程序猿閱讀 19,519評(píng)論 5 35
  • 專業(yè)考題類型管理運(yùn)行工作負(fù)責(zé)人一般作業(yè)考題內(nèi)容選項(xiàng)A選項(xiàng)B選項(xiàng)C選項(xiàng)D選項(xiàng)E選項(xiàng)F正確答案 變電單選GYSZ本規(guī)程...
    小白兔去釣魚閱讀 10,667評(píng)論 0 13
  • 今天的理論學(xué)習(xí),get和set兩種方法是php中最常用的方法之二。PHP也是面向?qū)ο蟮膕et方法是為屬性設(shè)置值的,...
    姬漢斯閱讀 212評(píng)論 0 1
  • 今天兒子和張夢(mèng)宇在張夢(mèng)宇媽媽的陪同下一起來到了沭河廣場(chǎng),人很多,孩子們跑的滿頭大汗,玩的很開心!
    孔凡乙閱讀 68評(píng)論 0 0
  • 我們店面平常對(duì)省內(nèi)顧客三天左右回訪,對(duì)省外的顧客5天左右回訪,詢問顧客是否收到訂購貨品、號(hào)碼是否合適等,但是有...
    德州萬達(dá)DDM張愛娟閱讀 308評(píng)論 2 1

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