美文网首页
ios多线程详解

ios多线程详解

作者: 721e472431a4 | 来源:发表于2018-06-11 01:17 被阅读17次

                                                ios多线程详解

    一、前言

    在ios中每个进程启动后都会建立一个主线程(UI进程),这个线程是其他线程的父线程。由于在ios中除了主线程,其他子线程都是独立于Cocoa Touch的。多线程的实现有以下几种方式:

    NSThread:

    (1)使用NSThread对象建立一个线程,非常方便。

    (2)但是!使用NSThread管理多个线程非常困难,不推荐使用。

    GCD--Grand Central Dispatch:

    (1)基于C语言的底层API。

    (2)用block定义任务,使用起来非常灵活方便。

    (3)提供了更多的控制能力以及操作队列中所不能使用的底层函数。

    NSOperation/NSOperationQueue:

    (1)是使用GCD实现的一套Object-C的API。

    (2)是面向对象的线程技术。

    (3)提供了一些在GCD中不易实现的特性,如:限制最大并发数量、操作之间的依赖关系。

    二、线程与进程

    1.进程

    进程是系统进行资源分配和调度的基本单位,每一个进程都有自己独立的虚拟内存空间。简单来说,进程是指在系统中正在运行的一个应用程序,每一个程序都是一个进程,并且进程之间是相互独立的,每个进程均运行在其专业且受保护的内存空间内。

    2.线程

    是程序执行流的最小单元,是系统独立调度和分派CPU的基本单位。

    一个进程中至少有一条线程,即主线程。创建线程的目的就是为了开启一条新的执行路径,运行指定的代码,与主线程中的代码同时执行。

    3.多线程

    计算机同一个时间执行多个线程,进而提升整体处理性能。

    原理:

    (1)同一时间,CPU只能处理1条线程,只有一条线程在工作。

    (2)多线程并发执行,其实是CPU快速的在多条线程中切换(调度)。

    (3)如果CPU调度线程的速度够快,就造成多线程同时执行多假象。

    优点:

    (1)能适当提高程序的执行效率。

    (2)能适当提高资源的利用率(CPU,内存利用率)。

    缺点:

    (1)开启新线程需要占用一定的内存空间(默认情况下,主线程占1M,子线程占512K)。如果开启大量的线程,会占用大量的内存空间,降低程序的性能。

    (2)线程越多,CPU在线程间切换(调度)上的开销就越大。

    注:主线程栈区的1M特别宝贵。不能杀掉一个线程!但可以暂停、休眠。

    三、NSThread的使用

    1.线程的创建

    NSThread创建线程有以下三种方法:

    [NSThread detachNewThreadSelector:(nonnull SEL)> toTarget:(nonnull id) withObject:(nullable id)]

    NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(doSomething) object:nil];

    - (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg

    NSThread对象的常见属性:

    NSThread类方法:

    (1)当前线程:

    int number = [NSThread currentThread];

    number == 1 表示主线程,number != 1表示后台线程

    (2)阻塞方法:

    休眠到指定时间

    [NSThread sleepUntilDate:[NSDate date]];

    休眠指定时长

    [NSThread sleepForTimeInterval:4.5];

    (3)其他类方法:

    退出线程

    [NSThread exit];

    当前线程是否为主线程

    [NSThread isMainThread];

    是否多线程

    [NSThread isMultiThreaded];

    返回主线程的对象

    NSThread *mainThread = [NSThread mainThread];

    2.线程的状态

    线程的状态如下图:

    (1)新建:实例化对象

    (2)就绪:向线程对象发送start消息,线程对象被加入"可调度线程池"等待CPU调度,detach方法和performSelectorInBackground方法会直接实例化一个线程对象并且加入"可调度线程池"。

    (3)运行:CPU负责调度"可调度线程池"中线程的执行,线程完成执行之前,其状态可能在"就绪"和"运行"之间切换。

    (4)阻塞:当满足某个条件时,可以使用休眠或锁阻塞线程执行,方法有sleepForTimeInterval,sleepUntilDate,@synchronized(self)x线程锁。线程进入阻塞状态下时,会被从"可调度线程池"中移出,CPU不再调度。

    (5)死亡:死亡后线程对象的isFinished属性为YES,如果是对线程发送cancel消息,线程对象的isCenceled属性为YES,死亡后stackSize==0,内存空间被释放。

    2.多线程的安全问题

    多个线程访问同一块资源进行读写,如果不加控制随意访问容易产生数据错乱,从而引发数据安全问题。为了解决这一问题,就有了加锁的概念。加锁的原理就是当有一个线程正在访问资源进行写的时候,不允许其他线程再访问该资源,只有当该线程访问结束后,其他线程才按顺序进行访问。对于读取数据,有些程序设计是允许多线程同时读的,有些不允许。 UIKit中几乎所有控件都不是线程安全的,因此需要在主线程中更新UI.

    解决多线程安全问题:

    (1)互斥锁

     注意:锁定1份代码只用1把锁,用多把锁是无效的

    @synchronized(锁对象) { 需要锁定的代码  }

    使用互斥锁,在同一时间,只允许一条线程执行锁中的代码。因为互斥锁的代价十分昂贵,所以锁定的代码范围应该尽可能吧小,只要锁住资源读写部分的代码即可。

    (2)使用NSLock对象

    (3)atomic加锁

    OC在定义属性的时候有nonatomic和atomic两种选择。

    atomic:原子属性,为setter方法加锁(默认就是atomic)。线程安全,但需要消耗大量资源。

    nonatomic:非原子属性,不会为setter方法加锁。非线程安全,但效率高。

    atomic加锁原理:

    ios开发的建议:

    所有属性都声明nonatomic。

    尽量避免多线程抢夺同一块资源。

    尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动端的压力。


    四、GCD的使用

    GCD(Grand Central Dispatch)伟大的中央调度系统,是苹果为多核并行运算提出的C语言并发技术框架。GCD会自动利用更多的CPU内核,会自动管理线程的生命周期(线程创建,调度任务,线程销毁),只需要告诉GCD想要如何执行什么任务,不需要编写任何线程管理代码。

    一些专业术语:

    dispatch:调度/派遣

    queue:队列,用来存放任务的先进先出(FIFO)的容器。

    sync:同步函数,只是在当前线程中执行任务,不具备开启新线程的能力。

    async:异步函数,可以在新的线程中执行任务,具备开启新线程的能力。

    concurrent:并发,多个任务同时进行。

    串行:一个任务执行完毕后,再执行下一个任务。

    1.GCD中的核心概念:

    任务: 任务就是要在线程中执行的操作。我们将要执行的代码用block封装好,然后将任务添加到队列容器中,并指定任务的执行方式。等待CPU从队列中取出任务放到对应的线程中执行。

    队列

    串行队列:一次只调度一个任务,一个任务完成后再调度下一个任务。

    并发队列:可以同时调度多个任务,调度任务的方式,取决于执行任务的函数,并发功能只有在异步函数下才有效。异步情况下,开启的新线程极限数量由GCD底层决定。

    如果在MRC下需要使用dispatch_release释放队列:

    主队列:负责在主线程上调度任务,如果在主线程上有任务执行,会等待主线程空闲后再进行调度。主队列用于UI以及触摸事件等的操作。

    全局并发队列: 由苹果API提供的,方便程序员使用多线程。

    全局并发队列的优先级:

    define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高优先级

    define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)优先级

    注意,自定义队列的优先级都是默认优先级

    define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低优先级

    define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台优先级

    全局并发队列与并发队列的区别:

    (1)全局并发队列没有队列名称。

    (2)在MRC中,全局并发队列不需要手动释放。


    执行任务的函数

    (1)同步函数(dispatch_sync)

    任务被添加到队列后,队列中的任务一个接着一个执行。

    在主线程中,向主队列添加同步任务,会造成死锁。

    在其他线程中,向主队列添加同步任务,则会在主线程中同步执行。

    (2)异步函数(dispatch_async)

    GCD的其他用法

    (1)延时执行

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

        // 2秒后异步执行这里的代码...

    });

    (2)一次性执行

    应用场景:保证某段代码在程序运行过程中只被执行一次,在单例模式中经常被用到。

    (3)调度组(队列组)

    五、NSOperation

    NSOperation是苹果推荐的并发技术,它提供了一些GCD不是很好实现的功能。NSOperation是基于GCD的面向对象的OC语言封装。相比GCD,NSOperation的操作更简单。NSOperation是一个抽象类,不能直接使用,而是使用它的子类。苹果为我们提供了其两个子类:NSInvocationOperation,NSBlockOperation.以及继承NSOperation的自定义子类。

    NSOperation的使用常常是配合NSOperationQueue来进行的。只要使用NSOperation子类创建的实例就能添加到NSOperationQueue中,一旦添加到队列,操作就会自动异步执行。如果没有添加到队列,而是使用start方法,则会在当前线程中执行。

    (1)NSInvocationOperation

    直接创建一个NSInvocationOperation对象,然后调用start方法会直接在主线程中执行。

    添加到NSOperationQueue中:

    (2)NSBlockOperation

    NSBlockOperation与NSInvocationOperation用法相同,只是创建的方式不同,它不需要去调用方法,而是直接使用代码块。这也使得NSBlockOperation比NSInvocationOperation更流行。

    (3)NSOperationQueue的一些高级操作

    NSOperationQueue的高级操作有:队列的挂起,队列的取消,添加操作的依赖关系和设置最大并发数量。

    最大并发数:

    线程的挂起:

    取消队列里的所有操作:

     六、三种线程技术比较

    1.NSThread

    优点:NSThread比其他两个轻量级,使用简单。

    缺点:需要管理自己的线程生命周期、加锁、睡眠以及唤醒等。

    2.GCD

    GCD是ios4.0以后才出现的并发技术

    使用方式:将任务添加到队列(串行/并行(全局))中,指定执行任务的方法(同步函数sync,异步函数async)。

    NSOperation无法做到的:延迟执行,队列组(NSOperation实现会比较复杂)。

    3.NSOperation

    NSOperation在ios2.0时就出现了(当时不好用,后苹果对其改造)

    使用方式:将操作(异步执行)添加到队列(并发/全局)中。

    提供了GCD不好实现的功能:最大并发数、取消所有任务、依赖关系。

    GCD是比较底层的封装,我们知道较低层的代码一般性能都是比较高的,相对于NSOperationQueue。所以追求性能,而功能够用的话就可以考虑使用GCD。如果异步操作的过程需要更多的用户交互和被UI显示出来,NSOperationQueue会是一个好选择。如果任务之间没有什么依赖关系,而是需要更高的并发能力,GCD则更有优势。

    尾语:

    高德纳的教诲:“在大概97%的时间里,我们应该忘记微小的性能提升。过早优化是万恶之源。”只有Instruments显示有真正的性能提升时才有必要用低级的GCD。

    相关文章

      网友评论

          本文标题:ios多线程详解

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