美文网首页
NSThread-详解

NSThread-详解

作者: Q2我没有疯 | 来源:发表于2019-03-22 02:37 被阅读0次

    当你想在线程中执行一个很长的任务,但又不希望它阻塞应用程序其余部分的执行时,子线程尤其有用,你可以将任务放在子线程中来执行,避免阻塞应用程序的主线程,让主线程处理用户界面和与事件相关的操作,子线程用于将大型任务划分为几个较小的任务,这能会使多核计算机上的性能提高。


    NSThread使用

    • 获取主线程&当前线程
        // 获取主线程
        NSThread *mainThred = [NSThread mainThread];
        // 获取当前线程
        NSThread *currentThred = [NSThread currentThread];
      
    • 线程的创建
    1. 这种创建方式需要手动启动线程
    - (void)viewDidLoad {
        [super viewDidLoad];
        // 创建一条线程(方式一)
        NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(runThread:) object:@"我是一条线程"];
        // 启动线程
        [thread start];
    }
    
    1. 以下几种方式创建线程会自动启动线程,但是出了方式二以外的几种创建方式都无法对线程进行详细的设置。
    // 方式二
       NSThread *thread = [[NSThread alloc] initWithBlock:^{
           
       }];
    // 方式三
       [NSThread detachNewThreadWithBlock:^{
              NSLog(@"thread=%@-------",[NSThread currentThread]);
       }];
    // 方式四
       [NSThread detachNewThreadSelector:@selector(runThread:) toTarget:self withObject:@"线程"];
    // 方式五
       [self  performSelectorInBackground:@selector(runThread:) withObject:@"线程"];
    
    
    • 常用属性
    // 优先级 0.0 ~ 1.0 默认 0.5 数值越大优先级越高
    @property double threadPriority ; 
    
    /** NSQualityOfService:
      NSQualityOfServiceUserInteractive:最高优先级,主要用于提供交互UI的操作,比如处理点击事件,绘制图像到屏幕上
      NSQualityOfServiceUserInitiated:次高优先级,主要用于执行需要立即返回的任务
      NSQualityOfServiceDefault:默认优先级,当没有设置优先级的时候,线程默认优先级
      NSQualityOfServiceUtility:普通优先级,主要用于不需要立即返回的任务
      NSQualityOfServiceBackground:后台优先级,用于完全不紧急的任务
    */
    @property NSQualityOfService qualityOfService; 
    
    // 线程名称(区别多条线程)
    @property (nullable, copy) NSString *name;
    
    // 线程正在执行
    @property (readonly, getter=isExecuting) BOOL executing;
    
    // 线程执行结束
    @property (readonly, getter=isFinished) BOOL finished;
    
    // 线程是否可以取消
    @property (readonly, getter=isCancelled) BOOL cancelled;
    
    • 常用方法
    -(void)start; // 启动线程
    
    -(BOOL)isMainThread; // 是否为主线程
    
    -(void)setName:(NSString *)n; // 设置线程名称
    
    -(void)cancel ;// 取消线程
    
    -(void)isExecuting; // 判断线程是否正在执行
    
    -(void)isCancelled; // 判断线程是否撤销
    
    -(void)isFinished; // 判断线程是否已经结束
    
    +(void)currentThread; // 获取当前线程
    
    +(BOOL)isMultiThreaded;  // 当前代码运行所在线程是否是子线程
    
    +(void)sleepUntilDate:(NSDate *)date;  //当前代码所在线程睡到指定时间
    
    +(void)sleepForTimeInterval:(NSTimeInterval)ti; //当前线程睡多长
    时间
    
    +(void)exit; // 退出当前线程
    
    • 线程的生命周期

    当线程中的任务执行完成过后被释放

    • 线程的状态
      1. 就绪状态:当前线程准备就绪,CPU调度当前线程--->变为运行状态。
      2. 运行状态:当前线程运行中,当CPU调度其他线程--->变为就绪状态。
      3. 阻塞状态:当前线程调用sleep方法,等睡眠时间结束--->变为就绪状态。
      4. 死亡状态:线程任务执行完成,移出可调度线程池,被释放掉。

    线程的安全

    • 如果多个线程(同时)访问同一资源时,很容易引发数据错乱和数据安全问题。


      线程安全.png

    总共有100张电影票,当售票员01和02同时获取到余票为一百张时,01卖出了80张余票为20张,02卖出了21张余票为79张,实际上他们卖出了101张但是总票数只有100张,这样就多出一个人没法看电影,这就是引发数据错乱。

    
    #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];
        
        // 总共设置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++) { // 模拟售票时间
                    self.totalCount = count - 1;
                }
                NSLog(@"%@卖了一张票还剩%zd",[NSThread currentThread].name,self.totalCount);
            }else{
                NSLog(@"售完了");
                break;
            }
        }
    }
    @end
    
    输出结果.png
    • 互斥锁 「@synchronized () { }」
      • 锁是必须全局唯一的
      • 注意加锁的位置。
      • 注意加锁条件(多线程访问同一资源)
      • 加锁的结果会导致线程同步(多条线程在同一条线上执行,按顺序执行任务)
    
    -(void)runThread:(id)obj{
     
        while (1) {
            // 锁:必须是全局唯一的
            @synchronized (self) {  // 互斥锁
                NSInteger count = self.totalCount;
                if (count>0) {
                    for (int i = 0; i<1000; i++) { // 模拟售票时间
                        self.totalCount = count - 1;
                    }
                    NSLog(@"%@卖了一张票还剩%zd",[NSThread currentThread].name,self.totalCount);
                }else{
                    NSLog(@"售完了");
                    break;
                }
            }
        }
          
    }
    
    互斥锁结果.png

    非原子属&性原子

    • nonatomic:非原子属性,不会为setter方法加锁,非线程安全,适合内存小的移动设备。
    • atomic:原子属性,为setter方法加锁(默认就是atomic),线程安全,需要耗费大量的资源。

    线程间通信

    • 一个线程传递数据给另一个线程
    • 在一个线程中执行完成任务,转到另一个线程中继续执行任务(列如子线程下载图片,主线程显示图片)
    • 常用方法
    // 从当前线程切换到主线程中
    -(void)performSelectorOnMainThread:(SEL)aSelector
                            withObject:(id)arg
                         waitUntilDone:(BOOL)wait;
        
    // 从当前线程切换到指定的线程
    -(void)performSelector:(SEL)aSelector
                  onThread:(NSThread *)thr
                withObject:(id)arg
             waitUntilDone:(BOOL)wait;
    

    相关文章

      网友评论

          本文标题:NSThread-详解

          本文链接:https://www.haomeiwen.com/subject/ulkkvqtx.html