多线程

一、进程、线程

1.1、 进程

  • 进程是一个具有一定独立功能的程序关于某次数据集合的一次运行活动,它是操作系统分配资 源的基本单元。
  • 进程是指在系统中正在运行的一个应用程序,就是一段程序的执行过程,我们可以理解为手机上 的一个 app。
  • 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内,拥有独立运行所需 的全部资源。

1.2、线程

  • 程序执行流的最小单元,线程是进程中的一个实体.
  • 一个进程要想执行任务,必须至少有一条线程.应用程序启动的时候,系统会默认开启一条线程,也就是主线程,又叫UI线程。

1.3、进程和线程的关系

  • 线程是进程的执行单元,进程的所有任务都在线程中执行
  • 线程是 CPU 分配资源和调度的最小单位
  • 一个程序可以对应多个进程(多进程),一个进程中可有多个线程,但至少要有一条线程
  • 同一个进程内的线程共享进程资源

二、多线程

  • 同一时间,CPU只能处理1条线程,只有1条线程在执行。多线程并发执行,其实是CPU快速地在多条 线程之间调度(切换)。如果 CPU 调度线程的时间足够快,就造成了多线程并发执行的假象。
  • 如果线程非常非常多,CPU会在N多线程之间调度,消耗大量的CPU资源,每条线程被调度执行的频次 会降低(线程的执行效率降低)。
  • 多线程的优点:
    1.能适当提高程序的执行效率
    2.能适当提高资源利用率(CPU、内存利用率)
  • 多线程的缺点:
    1.开启线程需要占用一定的内存空间(默认情况下,主线程占用 1M,子线程占用 512KB),如果开启大量的 线程,会占用大量的内存空间,降低程序的性能。
    2.线程越多,CPU 在调度线程上的开销就越大。
    3.程序设计更加复杂:比如线程之间的通信、多线程的数据共享

三、任务、队列

任务

就是执行操作的意思,也就是在线程中执行的那段代码。在 GCD 中是放在 block 中的。执行任务有两种 方式:同步执行(sync)和异步执行(async)
同步(Sync):同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的 任务完成之后再继续执行,即会阻塞线程。只能在当前线程中执行任务(是当前线程,不一定是主线程), 不具备开启新线程的能力。
异步(Async):线程会立即返回,无需等待就会继续执行下面的任务,不阻塞当前线程。可以在新的线程中 执行任务,具备开启新线程的能力(并不一定开启新线程)。如果不是添加到主队列上,异步会在子线程中 执行任务

队列

队列(Dispatch Queue):这里的队列指执行任务的等待队列,即用来存放任务的队列。队列是一种特殊 的线性表,采用 FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从 队列的头部开始读取。每读取一个任务,则从队列中释放一个任务

在 GCD 中有两种队列:串行队列并发队列。两者都符合 FIFO(先进先出)的原则。两者的主要区别是: 执行顺序不同,以及开启线程数不同。
串行队列(Serial Dispatch Queue): 同一时间内,队列中只能执行一个任务,只有当前的任务执行完成之后,才能执行下一个任务。(只 开启一个线程,一个任务执行完毕后,再执行下一个任务)。主队列是主线程上的一个串行队列,是 系统自动为我们创建的。
并发队列(Concurrent Dispatch Queue): 同时允许多个任务并发执行。(可以开启多个线程,并且同时执行任务)。并发队列的并发功能只有 在异步(dispatch_async)函数下才有效

四、iOS 中的多线程

主要有三种:NSThread、NSoperationQueue、GCD

NSThread:轻量级别的多线程技术

是我们自己手动开辟的子线程,如果使用的是初始化方式就需要我们自己启动,如果使用的是构造器方式 它就会自动启动。只要是我们手动开辟的线程,都需要我们自己管理该线程,不只是启动,还有该线程使 用完毕后的资源回收
image.png

performSelector...只要是 NSObject 的子类或者对象都可以通过调用方法进入子线程和主线程,其实这些 方法所开辟的子线程也是 NSThread 的另一种体现方式。 在编译阶段并不会去检查方法是否有效存在,如果不存在只会给出警告
image.png
需要注意的是:如果是带 afterDelay 的延时函数,会在内部创建一个 NSTimer,然后添加到当前线程的 Runloop 中。也就是如果当前线程没有开启 runloop,该方法会失效。在子线程中,需要启动 runloop(注 意调用顺序)
image.png

而 performSelector:withObject:只是一个单纯的消息发送,和时间没有一点关系。所以不需要添加到子线程的 Runloop 中也能执行

GCD 与 NSOprationQueue 对比

GCD 是面向底层的 C 语言的 API,NSOpertaionQueue 用 GCD 构建封装的,是 GCD 的高级抽象。

1、GCD 执行效率更高,而且由于队列中执行的是由 block 构成的任务,这是一个轻量级的数据结构,写起 来更方便
2、GCD 只支持 FIFO 的队列,而 NSOperationQueue 可以通过设置最大并发数,设置优先级,添加依赖关系 等调整执行顺序
3、NSOperationQueue 甚至可以跨队列设置依赖关系,但是 GCD 只能通过设置串行队列,或者在队列内添 加 barrier(dispatch_barrier_async)任务,才能控制执行顺序,较为复杂
4、NSOperationQueue 因为面向对象,所以支持 KVO,可以监测 operation 是否正在执行(isExecuted)、 是否结束(isFinished)、是否取消(isCanceld)

  • 实际项目开发中,很多时候只是会用到异步操作,不会有特别复杂的线程关系管理,所以苹果推崇的 且优化完善、运行快速的 GCD 是首选。
  • 如果考虑异步操作之间的事务性,顺序行,依赖关系,比如多线程并发下载,GCD需要自己写更多的 代码来实现,而 NSOperationQueue 已经内建了这些支持。
  • 不论是GCD还是NSOperationQueue,我们接触的都是任务和队列,都没有直接接触到线程,事实上 线程管理也的确不需要我们操心,系统对于线程的创建,调度管理和释放都做得很好。而 NSThread 需要我们自己去管理线程的生命周期,还要考虑线程同步、加锁问题,造成一些性能上的开销。

五、NSOperationQueue 的优点

NSOperation、NSOperationQueue 是苹果提供给我们的一套多线程解决方案。实际上 NSOperation、 NSOperationQueue 是基于 GCD 更高一层的封装,完全面向对象。但是比 GCD 更简单易用、代码可读性也更高。

1.可以添加任务依赖,方便控制执行顺序。
2.可以设定操作执行的优先级。
3.任务执行状态控制:isReady,isExecuting,isFinished,isCancelled

如果只是重写 NSOperation 的 main 方法,由底层控制变更任务执行及完成状态,以及任务退出 如果重写了 NSOperation 的 start 方法,自行控制任务状态
系统通过 KVO 的方式移除 isFinished==YES 的 NSOperation
4.可以设置最大并发量

NSOperation 和 NSOperationQueue

操作(Operation):
执行操作的意思,换句话说就是你在线程中执行的那段代码。
在 GCD 中是放在 block 中的。在 NSOperation 中,使用 NSOperation 子类 NSInvocationOperation、 NSBlockOperation,或者自定义子类来封装操作。

操作队列(Operation Queues):
这里的队列指操作队列,即用来存放操作的队列。不同于 GCD 中的调度队列 FIFO(先进先出)的原则。 NSOperationQueue 对于添加到队列中的操作,首先进入准备就绪的状态(就绪状态取决于操作之间的依赖关系),然后进入就绪状态的操作的开始执行顺序(非结束执行顺序)由操作之间相对的优先级决定(优 先级是操作对象自身的属性)。

操作队列通过设置最大并发操作数(maxConcurrentOperationCount)来控制并发、串行。
NSOperationQueue 为我们提供了两种不同类型的队列:主队列和自定义队列。主队列运行在主线程之上,而自定义队列在后台执行。

NSThread+runloop 实现常驻线程

NSThread 在实际开发中比较常用到的场景就是去实现常驻线程。

由于每次开辟子线程都会消耗cpu,在需要频繁使用子线程的情况下,频繁开辟子线程会消耗大量的 cpu,而且创建线程都是任务执行完成之后也就释放了,不能再次利用,那么如何创建一个线程可以 让它可以再次工作呢?也就是创建一个常驻线程。

首先常驻线程既然是常驻,那么我们可以用 GCD 实现一个单例来保存 NSThread。
image.png

这样创建的 thread 就不会销毁了吗?


image.png
并没有打印,说明 test 方法没有被调用。
那么可以用 runloop 来让线程常驻
image.png

这时候再去调用 performSelector 就有打印了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,710评论 4 376
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,839评论 2 308
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,295评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,776评论 0 223
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,198评论 3 297
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,074评论 1 226
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,200评论 2 322
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,986评论 0 214
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,733评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,877评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,348评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,675评论 3 265
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,393评论 3 246
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,209评论 0 9
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,996评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,212评论 2 287
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 36,003评论 2 280