美文网首页
二. NSThread基本使用

二. NSThread基本使用

作者: 面糊 | 来源:发表于2016-05-25 21:36 被阅读21次

一. 线程的创建

  1. 创建线程并且手动开启, 同时在这条线程执行selector的任务

     // 1. 创建线程对象
     NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(task1:) object:@"创建线程方式1"];
     // 2. 为线程添加标识   
     [thread1 setName:@"thread1"];           
     // 3. 设置线程的优先级(优先级高的线程,会优先执行)       
     [thread1 setThreadPriority:0.5];       
     // 4. 开始执行
     [thread1 start];
    
  2. 分离出一条子线程, 同时在这条线程执行selector的任务

     // 2. 分离子线程
     [NSThread detachNewThreadSelector:@selector(task1:) toTarget:self withObject:@"创建线程方式2"];           
    
  3. 开启一条后台线程, 同时在这条线程执行selector的任务(注意, 该方法是由当前控制器调用的)

       // 3. 创建后台线程
       [self performSelectorInBackground:@selector(task1:) withObject:@"创建线程方式3"];
    
  4. 自定义线程

    • 创建一个NSThread的子类
    • 在类中重写-(void)main方法, 将这个线程需要执行的任务, 在这个方法中实现
    • 使用alloc/init创建这个自定义线程, 当使用start执行这个线程的时候, 系统会自动调用main方法来执行其中的任务
  5. 创建线程方法的优缺点

    • 手动创建线程, 可以获取线程对象, 可以对这个线程对象进行详细的设置, 如标识/优先级等
    • 而采用分离子线程或者开启后台线程的方法, 使用方法简单, 可以直接开启一个子线程去执行耗时任务, 但是由于无法获取到这个线程对象, 因此无法对其进行设置

二. 线程的生命周期

  • 线程的五种状态
    • New: 一个刚刚新建出来的线程, 此时没有任何任务在执行当中
    • Runable: 处于可调度线程池的线程, 只有在线程池中的线程, 才可以被CPU调度
    • Running: 使用了-(void)start方法, 被激活工作, 且正在被CPU调度的线程, 一个线程如果还未完成它所有的任务, 那么他就会一直在Runable和Running两个状态中相互切换
    • Blocked: 调用了[NSThread sleepForTimeInterval]方法或等待同步锁的放行时的状态, 此时线程暂时被移除可调度线程池, 但是并没有被销毁掉, 当sleep的时间到了或同步锁放行, 就会恢复Runable状态
    • Dead: 当一条线程的任务执行完毕/异常或强制退出exit方法时, 这条线程就会被销毁, 销毁后的线程与对象一样, 不能再次使用

三. 线程安全

  1. 多线程应用时的安全隐患
    • 当多个线程, 出现访问同一块资源的时候, 这样会导致在存取的过程中, 被取资源中保存的值由于可能同时在修改, 导致值发生错误

      // 例子: 三个售票员同时售票
        - (void)sale {
           while (1) {
               @synchronized(self) {  // 如果这里不增加线程锁, 就会导致存取错误
                   // 1. 检查余票
                   int count = self.count;
                   if (count > 0) {
                       // 演示耗时操作
                       for (int i = 0; i < 100000; i++) {
                           // 如果线程的任务中有耗时操作,就有可能引起共同访问一块资源导致数据错误
                           // 因此这时需要加入线程锁
                       }
                       // 访问属性
                       self.count = count - 1;
                       NSLog(@"%@卖出去了一张票,还剩%d张", [NSThread currentThread], self.count);
                       
                   } else {
                       NSLog(@"票已经卖完");
                       [NSThread exit];
                   }
               }
           }
        }
      
    • 解决方法: 在线程要访问资源之前, 增加一个互斥锁
      @synchronized(锁对象){ 需要锁定的代码 }

    • 互斥锁:

      • 通常锁对象, 是全局唯一的一个对象, 一般使用NSObject作为对象的类型
      • 注意点
        1. 一份代码, 只能使用同一把互斥锁
        2. 锁对象本身有两种状态: 打开/关闭
        3. 当互斥锁关闭的时候, 队列中的线程就会进入Blocked状态, 直到互斥锁打开, 该线程才会进入运行状态
        4. 锁对象可以使用当前的控制器, 也就是self
        5. 加锁的位置需要注意, 位置不同, 执行的代码也会不同
        6. 加锁的前提条件: 只有出现多个线程同时访问同一块资源的时候, 才需要使用互斥锁进行保护
        7. 互斥锁的使用, 会增加额外的资源消耗, 所以能不使用就不要使用, 尽量避免出现多线程抢夺资源
        8. 互斥锁会造成线程同步: 每个线程会在锁的外面排队执行任务
        9. 但是队列中的线程是异步的: 到底是哪条线程访问锁内的资源, 顺序是不确定的

四. 原子和非原子性

  1. atomic:

    • 属性为原子性, 该关键字会为setter方法增加一个互斥锁, 因此它是线程安全的
    • 但是他会消耗大量的内存资源, 并且在我们的开发过程中, 很少发生多线程访问同一个属性的情况, 因此基本不会使用这个关键字
  2. nonatomic:

    • 属性为非原子性, 此关键字不会为setter方法增加互斥锁, 因此是非线程安全的, 适合内存小的移动设备, 即iOS开发中属性主要使用的关键字,
    • 因此, 在日常开发中, 尽量要避免多线程抢夺资源的情况, 加锁/资源抢夺的业务逻辑通常会交由服务器端来处理, 即每次只能接收/发送一份网络请求

五. 线程间的通信

  1. 需求:
    • 开启条子线程, 并且下载一张图片

    • 将获得到的图片, 在主线程中设置给imageView

    • 注意点: 遵循耗时操作交给子线程, UI操作回到主线程

        // 0. 创建子线程执行下载任务线程
        NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download) object:nil];
        [thread start];
        
        - (void)download {
        // 计算时间(绝对时间)
        CFTimeInterval start = CFAbsoluteTimeGetCurrent();    
        // 1. 创建url路径
        NSURL *url = [NSURL URLWithString:@"http://dimg07.c-ctrip.com/images/tg/946/212/497/81b56770ed4544a6a8a1125fb381753d_C_640_640.jpg"];
        // 2. 将图片转换为二进制数据
        NSData *data = [NSData dataWithContentsOfURL:url];    
        // 3. 转换格式(二进制 -> UIImage)
        UIImage *image = [UIImage imageWithData:data];    
        // 4. 回主线程设置图片
        //    [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:NO]; // 该方法可以直接使用主线程去执行任务
        [self performSelector:@selector(showImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];    
        // 获取绝对时间
        CFTimeInterval end = CFAbsoluteTimeGetCurrent();    
        NSLog(@"%f", end - start);
        }

相关文章

  • 二. NSThread基本使用

    一. 线程的创建 创建线程并且手动开启, 同时在这条线程执行selector的任务 // 1. 创建线程对象 NS...

  • iOS学习笔记_NSThread基本使用

    一、NSThread基本使用 二、 NSThread的创建 1.创建线程(需要手动启动线程) 2.分离子线程,自动...

  • IOS多线程的方式

    pthread的基本使用(需要包含头文件) 需要#import 3 NSThread (1)NSThread的基本...

  • iOS多线程与网络(1)--基本概念

    1 基本概念 2 pthread 3 NSThread (1)NSThread的基本使用 (2)设置线程的属性 (...

  • iOS 多线程(三)NSThread的使用

    一、NSThread基本介绍   NSThread是OC中封装程度最小最轻量级的,使用更灵活,基本使用比较简单,但...

  • 第一天

    第1天 1 基本概念 2 pthread 3 NSThread (1)NSThread的基本使用 (2)设置线程的...

  • 多线程学习总结

    第1天 1 基本概念 2 pthread 3 NSThread (1) NSThread的基本使用 (2)线程的状...

  • 网络多线程

    第1天 1 基本概念 2 pthread 3 NSThread (1)NSThread的基本使用 (2)设置线程的...

  • iOS—多线程的基本概念及实现方式

    第1天 1 基本概念 2 pthread 3 NSThread (1)NSThread的基本使用 (2)设置线程的...

  • IOS多线程

    1.基本概念 2.pthread 3.NSThread (1)NSThread的基本使用 (2)设置线程的属性 (...

网友评论

      本文标题:二. NSThread基本使用

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