多线程(2)——NSThread

iOS中实现多线程的四种方案

  • pthread
  • NSThread
  • GCD
  • NSOpreation

Pthread:这种纯c语言的就别用了吧,还要自己管理线程的生命周期,果断不用
NSThread:这个虽然自己管理线程的生命周期,但这是面向对象的,OC语言,不常用到但要会用
GCD:这个很重要、很重要、很重要 自动管理线程的生命周期 使用率非常非常频繁
NSOpreation:这个也很重要,必须掌握,没什么好说的,也是自动管理线程的生命周期,使用率也非常高

简单了解下Pthread

只要create一次就会创建一个新的线程
系统会自动在子线程中调用传入的函数

  /*
第一个参数: 线程的代号(当做就是线程)
第二个参数: 线程的属性
第三个参数: 指向函数的指针, 就是将来线程需要执行的方法
第四个参数: 给第三个参数的指向函数的指针 传递的参数
一般情况下C语言中的类型都是以 _t或者Ref结尾
  */
pthread_t threadId;
// 只要create一次就会创建一个新的线程
pthread_create(&threadId , NULL, &demo, "hq");

关于NSThread

  • NSThread对比后面两种是相对轻量级的,更直观地控制线程对象
  • 但需要自己管理线程的生命周期、同步、加锁问题,在性能上会有一定的消耗
  • 注意:一个NSThread对象就代表一条线程

创建方法

** 第一种方法 ** (手动开启线程)

// 线程一启动,就会在线程thread中执行self的run方法

// 1.创建线程
NSThread *thread = [[NSThread alloc] initWithTarget:selfselector:@selector(run) object:nil];

// 2.设置线程的优先级(0.0 - 1.0,1.0最高级)  
thread.threadPriority = 1; 

// 3.启动线程
[thread start];

** 第二种方法 ** (自动开启线程)

// detachNewThreadSelector: 不用手动调用start方法, 系统会自动启动 没有返回值, 不能对线程进行更多的设置

[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];

** 第三种方法 ** (隐式创建并启动线程)

// 系统就会自动创建一个子线程, 并且在子线程中自动执行self的@selector方法

[self performSelectorInBackground:@selector(run) withObject:nil];

NSThread用法

  • 主线程相关用法
 + (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; // 是否为主线程
+ (BOOL)isMainThread; // 是否为主线程
NSThread *main = [NSThread mainThread];
  • 获得当前线程
NSThread *current = [NSThread currentThread];
  • 获取线程的名字

    - (void)setName:(NSString *)name;
    - (NSString *)name;
    

线程的状态

  • 创建出来 -> 新建状态
  • 调用start -> 准备就绪
  • 被CPU调用 -> 运行
  • sleep -> 阻塞
  • 执行完毕, 或者被强制关闭 -> 死亡
启动线程
- (void)start; 
// 进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态

阻塞(暂停)线程
// sleep方法是一个类方法, 所以说明在哪个线程中调用, 就会给哪个线程设置暂时
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)time;

// 暂停1s  
[NSThread sleepForTimeInterval:1]; 
[NSThread sleepUntilDate:[NSDate dateWithTimeInterval:1 sinceDate:[NSDate date]]];
强制停止线程
// 进入死亡状态
+ (void)exit;
注意:一旦线程停止(死亡)了,就不能再次开启任务

多线程的安全隐患及解决措施

  • 资源共享
    • 一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件等
    • 当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题
  • 解决措施
    • 互斥锁
    • 互斥锁的使用前提:多条线程抢夺同一块资源
    • 注意:锁定1份代码只用1把锁,用多把锁是无效的
  • 使用格式
// (锁对象self)
@synchronized
{ 
    // 需要锁定的代码      
}
        /*
         只要被@synchronized的{}包裹起来的代码, 同一时刻就只能被一个线程执行
         注意:
         1. 只要枷锁就会消耗性能
         2. 加锁必须传递一个对象, 作为锁
         3. 如果想真正的锁住代码, 那么多个线程必须使用同一把锁才行
         4. 加锁的时候尽量缩小范围, 因为范围越大性能就越低
         */
  • 互斥锁的优缺点

    • 优点:能有效防止因多线程抢夺资源造成的数据安全问题
    • 缺点:需要消耗大量的CPU资源
  • 线程同步

    • 线程同步的意思是:多条线程在同一条线上执行(按顺序地执行任务)
      互斥锁,就是使用了线程同步技术

原子和非原子属性

  • OC在定义属性时有nonatomic和atomic两种选择
    • atomic:原子属性,为setter方法加锁(默认就是atomic)
    • nonatomic:非原子属性,不会为setter方法加锁
  • 原子和非原子属性的选择
    • atomic:线程安全,需要消耗大量的资源
    • nonatomic:非线程安全,适合内存小的移动设备
  • iOS开发的建议
    • 所有属性都声明为nonatomic
    • 尽量避免多线程抢夺同一块资源
    • 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力

注意点: atomic系统自动给我们添加的锁不是互斥锁/ 自旋锁

  • 自旋锁和互斥锁对比
    • 相同点:都能够保证多线程在同一时候, 只能有一个线程操作锁定的代码
    • 不同点
      • 如果是互斥锁, 假如现在被锁住了, 那么后面来得线程就会进入”休眠”状态, 直到解锁之后, 又会唤醒线程继续执行
      • 如果是自旋锁, 假如现在被锁住了, 那么后面来得线程不会进入休眠状态, 会一直傻傻的等待, 直到解锁之后立刻执行
      • 自旋锁更适合做一些较短的操作

线程间的通信

  • 什么叫做线程间通信

    • 在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信
    • 比如在主线程添加imageView,在子线程中下载图片,然后又回到主线程中显示图片
    • 注意点: 更新UI一定要在主线程中更新
  • 线程间通信的体现

    • 1个线程传递数据给另1个线程
    • 在1个线程中执行完特定任务后,转到另1个线程继续执行任务
  • 在当前线程

[self performSelector:@selector(run) withObject:nil]; 
  • 在主线程

    [self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];
    
  • 在指定线程

[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];  

// waitUntilDone的含义: 如果传入的是YES: 那么会等到主线程中的方法执行完毕, 才会继续执行下面其他行的代码 如果传入的是NO: 那么不用等到主线程中的方法执行完毕, 就可以继续执行下面其它行的代码

线程间的通信最常见的就是在子线程中做耗时操作,然后回到主线程刷新UI

关于NSThread的总结

你可能感兴趣的:(多线程(2)——NSThread)