重修笔记之多线程NSThread

作者: iOS_July | 来源:发表于2018-03-11 12:08 被阅读27次

一、什么是多线程

  • 多线程是指从软件或者硬件上实现多个线程并发执行的技术。
  • 由于一条线程同一时间只能处理一个任务,所以线程里的任务必须按顺序执行。
  • 如果遇到耗时操作(网络通信、耗时计算、音乐播放等),等上一个操作完成再执行下一个任务,在此段时间内,用户得不到任何响应,这个体验无疑是极糟的。
  • 因此,在iOS编程中,通常将耗时操作单独放在一个线程中,而把用户交互的操作放在主线程中

二、进程与线程

  • 进程:
进程是指系统中正在运行的应用程序。这个'运行中的程序'就是一个进程。
每个进程都拥有着自己的地址空间。

进程有3个主要特征:

独立性:  
进程是一个能够独立运行的基本单位,它既拥有自己独立的资源,又拥有着自己私有的地址空间。
在没有经过进程本身允许的情况下,一个用户的进程是不可以直接访问其他进程的地址空间的。 

动态性:    
进程的实质是程序在系统中的一次执行过程。
程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。
在进程中加入了时间的概念,它就具有了自己的生命周期和各自不同的状态,进程是动态消亡的。
  
并发性:  
多个进程可以在单个处理器中同时进行,而不会相互影响。

  • 线程:
多线程扩展了多进程的概念,使得同一进程可以同时并发处理多个任务。
一个程序的运行至少需要一个线程,一个进程想要执行任务,也必须要有至少一个线程,而这个线程就被称作主线程。 
通常来说,只有一个主线程。
当进程被初始化后,主线程就被创建了,主线程是其他线程最终的父线程,所有界面的显示操作必须在主线程进行。

三、多线程的优势

  1. 进程间不能共享内存,但线程之间的共享内存是很容易的。

  2. 当硬件处理器的数量有所增加时,程序运行的速度更快,无需做其他调整。

  3. 充分发挥了多核处理器的优势,将不同的任务分配给不同的处理器,真正进入“并行运算”的状态。

  4. 将耗时、并发需求高的任务分配到其他线程执行,而主线程则负责统一更新界面,这样使得应用程序更加流畅,用户体验更好。

四、多线程的劣势

  1. 开启线程需要占用一定的内存空间[默认情况下,主线程最大占用1M的栈区空间、子线程最大占用512K的栈区空间],如果要开启大量的线程,势必会占用大量的内存空间,从而降低程序的性能。
  2. 开启的线程越多,CPU在调度线程上的开销就越大,一般最好不要同时开启超过5个线程
  3. 程序的设计会变得更加复杂,如线程之间的通信、多线程间的数据共享等。

五、线程的串行和并行

  • 串行:
如果在一个进程中只有一个线程,而这个进程要执行多个任务,
那么这个线程只能一个一个的按照顺序执行这些任务,也就是说,
在同一时间内,一个线程只能执行一个任务,这样的线程执行方式称为线程的串行。
  • 并行:
如果一个进程中包含的线程不止一条,
每条线程之间可以并行执行不同的任务,这叫做线程的并行,也叫多线程。
  • 补充:
假如一个进程有3个线程,每个线程执行1个任务,3个下载任务没有先后顺序。可以同时执行。 
同一时间,CPU只能处理一个线程,也就是只有一个线程在工作。
由于CPU快速的在多个线程之间调度,人眼无法察觉到,就造成了多线程并发执行的假象。

六、线程的状态

当线程被创建并启动之后,既不是一启动就进入执行状态,也不是一直处于执行状态,即便程序启动运行之后,它也不可能一直占用CPU独自运行。

由于CPU需要在多个线程之间进行切换,造成了线程的状态会在多次运行、就绪之间进行切换。

线程的状态主要有5个:

1. 新建New        
当程序新建一个线程之后,该线程就处于新建状态。
和其他对象一样,只是由系统分配了内存,并初始化了内部成员变量的值。
此时的线程没有任何动态特征

2. 就绪Runable
当线程被start之后,该线程就处于就绪状态。
系统会为其创建 方法调用的栈和 程序计数器。

3. 运行Running
当CPU调度当前线程的时候,将其他线程挂起,当前线程变为运行状态。
当CPU调度其他线程时,当前线程回到就绪状态。
测试线程是否在运行,调用isExecuting方法,若返回YES,则处于运行状态。

4. 终止Exit
* 线程执行方法执行完成,线程正常结束
* 线程执行的过程出现异常,线程崩溃结束
* 直接调用NSThread类的exit方法,终止当前正在运行的线程
* 测试线程是否结束,调用isFinished方法判断,若返回YES,则已终止。

5. 阻塞Blocked    
如果当前正在执行的线程需要暂停一段时间,并进入阻塞状态,通过NSThread类的两个方法:

//让当前执行的线程暂停到date参数代表的时间,并且进入阻塞状态
+ (void)sleepUntilDate:(NSDate *)date;

//让正在执行的线程暂停ti秒,并进入阻塞状态
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

七、线程间的安全隐患

进程中的一块资源可能会被多个线程共享,也就是多个线程可能访问同一块资源,这里的资源包括对象变量文件等。
当多个线程同时访问同一块资源时,会造成资源抢夺,很容易引发数据错乱数据安全的问题。
为了解决这个问题,实现数据的安全访问,可以使用线程间的加锁

  • 数据混乱示例:
/** 售票处理 */
- (void)saleTickets{
    while (true) {
        //模拟延时
        [NSThread sleepForTimeInterval:1.0];
        
        //判断是否还有票
        if (self.leftTickets > 0) {
            self.leftTickets--;
            
            NSLog(@"%@卖了一张票,还剩下%lu张票",[NSThread currentThread].name,self.leftTickets);
        } else{
            NSLog(@"票已售完");
            return;
        }
    }
}


-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //设定总票数
    self.leftTickets = 100;
    //创建3个线程,启动后执行saleTickets方法卖票
    NSThread *t1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
    t1.name = @"1号窗口";
    [t1 start];
    
    NSThread *t2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
    t2.name = @"2号窗口";
    [t2 start];
    
    NSThread *t3 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets) object:nil];
    t3.name = @"3号窗口";
    [t3 start];
}
未加锁.png

可以看到,由于3个线程的并发操作,同一时间抢夺一个资源leftTickets,造成了剩余票数统计的混乱。

  • 加锁示例:
@synchronized (obj)
{
    //插入被修饰的代码块
}
  • 使用同步锁的时候,要尽量让同步代码块包围的代码范围最小,而且要锁定共享资源的全部读写部分的代码。

obj就是加锁对象,添加了锁对象后,锁对象实现了对多线程的监管,保证同一时刻只有一个线程执行,当同步代码块执行完成后,锁定对象就会释放同步监视器的锁定。

/** 售票处理 */
- (void)saleTickets{
    while (true) {
        //模拟延时
        [NSThread sleepForTimeInterval:1.0];
        
        //判断是否还有票
        @synchronized(self){
            if (self.leftTickets > 0) {
                self.leftTickets--;
                
                NSLog(@"%@卖了一张票,还剩下%lu张票",[NSThread currentThread].name,self.leftTickets);
            } else{
                NSLog(@"票已售完");
                return;
            }
        }
    }
}
加锁.png

线程添加同步锁后,实现了线程的同步运行,也就是说,使多线程按顺序执行任务。需要注意的是,同步锁会消耗大量的CPU资源。

相关文章

  • 重修笔记之多线程NSThread

    一、什么是多线程 多线程是指从软件或者硬件上实现多个线程并发执行的技术。 由于一条线程同一时间只能处理一个任务,所...

  • 多线程-NSThread

    多线程学习笔记 - NSThread NSThread的集中创建方法 1.方法一: NSThread对象创建 需要...

  • iOS开发之多线程GCD

    前言 iOS开发之多线程理论部分 NSOperation NSThread 什么是GCD 全称是Grand Cen...

  • iOS开发之多线程NSThread

    线程是用来执行任务的,线程彻底执行完任务A才能去执行任务B。为了同时执行两个任务,产生了多线程。我打开一个视频软件...

  • 7.2 多线程-NSThreed

    多线程-NSThreed.png NSThread案列 NSThread线程之间的通讯 NSThread线程的优先...

  • iOS多线程篇:NSThread

    iOS多线程篇:NSThread iOS多线程篇:NSThread

  • iOS开发之多线程笔记(三)之NSThread

    NSThread 官方文档上的介绍翻译过来的大致意思就是:概述当你有一个方法需要在它自己的线程中运行的时候可以使用...

  • iOS 多线程

    iOS使用线程的方式 pthread NSThread GCD NSOperation NSThread线程的创建...

  • iOS多线程之NSThread

    什么是NSThread NSThread是一种轻量级多线程,一个NSThread对象代表一个线程,需要手动管理线程...

  • iOS进阶之多线程--NSThread详解

    NSThread简介 NSThread是苹果官方提供面向对象操作线程的技术,简单方便,可以直接操作线程对象,不过需...

网友评论

本文标题:重修笔记之多线程NSThread

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