iOS多线程基础
在iOS中,多线程的概念应该算是比好理解的了.并没有想象中那么难搞.简单的概念像线程/进程这些概念就不说了.
多线程其实真对单核的CPU来设计的.CPU同一时间只能执行一条线程.而多线程就是让CPU快速的在多个线程之间进行调度
多线程优点:
能够适当的提高程序的执行效率
能够适当提高资源利用率
缺点:
开启线程需要一定的内存空间,默认的一条线程占用栈区内存512KB
线程过多会导致CPU在线程上调度上的开销比较大
程序设计比较复杂,比如线程间的通信/多线程的数据共享
iOS应用程序中都是一个主线程,也成为UI线程
那么主线程的作用就是用来更新UI,显示/刷新UI界面
处理UI时间: 比如点击/滚动/拖拽/手势
注意:不能将耗时操作放在主线程,这样会给用户一种卡的"体验"
说下串行,并行,其实很简单就像你用浏览器下载和用迅雷下载,浏览器下载一般都是一个接一个的去下载.而迅雷就会同时去下载你所需要的所有东西.
简单入门级的多线程演示:
touchBegin{
[self performselector:@selector(nsoperation) withObject:nil ]
}
- (void) nsoperation {
for (int i = 0 , i < 50000 , i++){
nslog(" %d %d",i, [NSTread currentThread]);
}
}
iOS多线程技术--pthread
pthread是一套C语言的多线程API 使用难度比较大 需要程序员手动管理线程的生命周期 能够跨平台/可移植 能够在UNIX/LINX/Windows 下运行
但是使用起来比较困难,参数比较难懂 可参照 http://www.baidu.baike.com
iOS多线程--NSThread
是一套OC框架 使用更加面向对象,简单易用,可直接操作线程对象 偶尔使用
需要程序员手动管理线程的声明周期
一般用来查看当前线程是否是主线程:
=1 当前线程是主线程,!=1 当前线程是在子线程
iOS多线程--GCD
旨在代替NSThread多线程技术,充分利用设备的多核
也是一套基于C语言的API 不需要程序员手动管理线程的声明周期 经常使用
iOS多线程--NSOperation
是一套OC的API,是对GCD的封装 使用更加面向对象 不需要程序员手动管理线程的声明周期
比GCD多一些更加简单实用的功能 也是经常使用 特别是在iOS4以后
iOS多线程中的锁
互斥锁:
@synchronized 是为了保证其中代码只有一天线程来执行
所以@synchronized的范围是越小效率才高
@synchronized()其中的参数 就是能够加锁的任意ocject对象
局部变量,是每一条线程单独拥有的,所以无法加锁 锁一定要是所有线程共享的对象
一般使用self (全局的也是可以的)
自旋锁:
自旋锁存在于原子属性的内部
自旋锁的特点就是当发现有其他线程正在执行锁定的代码,该线程会用死循环的方式去等待锁定代码执行完毕
atomic(原子属性): 默认是线程安全的 当多个线程写入属性时,保证同一时间只有一条线程能够执行写入操作
线程间的通讯
在NSThread中线程间的通讯
方法: performSelectorOnMainThread: with object: waitUnitDone:
三个参数: 方法选择器,将该方法放到主线程执行
传递给方法的参数
是否等待被调用方法执行完成, 有可能也会等待调用方法执行完成! 几率极小!
NSThread的三种线程创建方法:
1. NSThread * thread = [[NSThread alloc]initWithTarget: selector: object:] 创建初始化一条线程
启动线程 [thread start]
2. [NSThread detachNewThreadSelector: toTarget: withObject:] 会立即执行selector方法
就是会分离一个子线程取执行selector方法
3.[self performSelectorInbackground: withObject: ]
这是NSObject的一个分类方法,所有NSObject都可以使用此方法在其他线程执行方法,一旦指定,就会立即在后台线程执行方法 使用比较灵活
介绍下NSThread中的几个重要属性:
name: 一般在比较复杂的商业应用中会设置线程的名字,因为可以快速找出崩溃线程
isMainThread: 这个是用来检测是否是主线程的属性
threadPriority: 这个属性是用来设置线程的优先级(线程的优先级是 用一个浮点数来表示: 0~1.0 , 默认线程的优先级是0.5) 注意: 设置线程的优先级高,只是CPU会优先调用该线程,调用该线程的可能性会高,并不是会将该线程执行完后再去执行别的线程 .
线程的状态属性:
isExecuting: 只读属性,线程是否正在运行
isFinished: 只读属性,线城是否完成
isCancelled: 只读属性,线程是否被取消
GCD--Grand Central Dispatch
是苹果公司为 多核的并行运算提出的 解决方案
GCD是轻量级的多线程技术
系统会自动管理线程的生命周期
GCD中2个核心概念:
任务: 同步/异步
队列: 串行/并发
程序员只需做两件事: 1. 定制任务,确定需要做的事情
2. 将任务添加到队列中( GCD会自动将任务从队列中取出,放到对应的线程, 任务的取出是遵循FIFO原则的:先进先出,后进后出)
调度任务,执行任务的方法: 同步/异步
同步: 一个任务没有执行完,就不会执行下一个任务
异步: 不用等待当前执行任务完成,就可以执行下一个任务
任务队列组合:
1.串行队列,同步任务
不会开启线程,顺序执行任务
2.串行队列,异步任务
会开启一条线程,顺序执行任务
3.并发队列,异步任务
会开启多条线程,每条线程都会执行任务,所以不会顺序
4.并发队列,同步任务
不会开线程,,顺序执行
小结: - 开不开线程,取决于任务是同步还是异步,同步不开,异步开
- 开几条线程,取决于队列,串行开一条,并发开多条(异步任务)
主队列特点: 主队列不是主线程
主线程上有任务,主队列不会调度队列中的任务
主队列异步任务 : 等待主线程上的任务执行完毕会执行主队列中的任务
主队列同步任务 : 等待主线程上的任务执行完毕,同步任务:当前任务不执行完毕,不会执行下一条任务
所以会造成互相等待,死锁!!!
同步任务的特点:
- 可以在队列调度多个异步任务之前,指定一个同步任务,让所有异步任务等待同步任务执行完毕,这就是依赖关系
全局队列:(其实就是并发队列<感觉是根据任务的同步/异步来确定的>)
就是给程序员使用的,方便程序员使用的
全局队列和并发队列的区别:
1>名称(队列后边的参数<是个字符串其实就是队列的名称>),并发队列有名称,适合商业级软件跟踪错误报告
2>release , 在MRC开发时,并发队列需要使用 dispatch_release(q);
目前绝大多数的软件都会使用全局队列. 比如比较优秀的第三方框架会使用自定义的并发队列!
全局队列和串行队列的选择
全局队列: 并发,能够调度多个线程,执行效率高
- 比较费电
串行队列: 一个接一个,只能够开启一条线程,执行效率比较低
- 省电,省钱,省流量
所以选择是根据用户连网状态来选择:
- WIFI,可以多开线程, 6条
- 3G/4G,尽量少开线程,2~3条
延时操作:
dispatch_after(dispatch_time,(int64_t)(delayInSeconds * NSEC_PER_SEC)),
dispatch_get_main_queue(),^{
});
一次性操作: 苹果提供了一个一次性执行的机制,不仅能够保证只被执行一次,而且是"线程安全"的
dispatch_once()
使用互斥锁也可以,但是效率低
调度组:
dispatch_group_t
可以将多个任务放在同一个调度组中,同时监听这多个任务的下载,最后用dispatch_group_notify来通知
其实就是用一个调度组,可以监听全局队列调度的任务,执行完毕后,在主队列执行最终处理
dispatch_group_notify本身是异步的,这个还可以用另一种方法还实现是:dispatch_group_wait()其中第二个参数就是等待时间,一般写DISPATCH_TIME_FOREVER
第二种使用方法,经常出现在三方框架中:AFN
dispatch_group_enter() 进入群组
dispatch_group_leave() 退出群组
这两个方法必须成对出现
NSOperation
核心概念就是 将"操作"添加到"队列"
NSOperation是一个抽象类,不能直接使用!
它是对GCD的面向对象的的封装
目的:定义子类共有的属性和方法
子类: - NSInvocationOperation
- NSBlockOperation
NSInvocationOperation:
只需要创建队列,执行start方法,就可以使用.
本质上就是GCD中的并发队列, 异步任务的执行
NSBlockOperation:
可以直接来一个全局的队列属性,懒加载之后,可以对所有的NSOperation的子类,添加到队列!
回主线程,直接[NSOperationQueue mainQueue]addOperationWithBlock:^ 就可以回主线程更新UI了非常简单好用
最大并发操作数:
从iOS8开始,无论是GCD还是NSOperation,都会开启很多线程,在iOS7以前GCD通常只会开启5~6条线程
线程数的增加说明了:
1 >底层的线程池变大了,能够拿到的线程资源更多了!
2 >对控制同时并发的线程数,要求更高了.
暂停/继续/取消全部操作:
暂停/继续: NSOperation中的属性isSuspended 是否挂起
可以对isSuspended判断,判断队列是否挂起,来做任务的暂停或者继续.
1.在设置队列的挂起属性(isSuspended)时,并不会判断队列中是否有操作
所以如果不希望用户有困惑,可以提前做判断,就是对队列的operationCount属性进行判断
2.在暂停的时候,队列中的操作数是包括正在执行的操作,再次继续的时候,如果之前正在执行的操作已经完成,那么队列中的操作数就只有没有调度的操作
取消所有的操作:
通过设置队列的cancelAllOperations属性,可以取消队列的所有操作
注意: 这个取消属性,也是不会取消正在执行的操作
依赖关系:
NSOperation提供了依赖关系
NSOperation的所有操作都是异步的,但是为了建立操作之间的依赖关系,所以提供了dependency的功能
GCD中可以通过同步任务来实现,也可以通过串行队列来实现
先设置以来关系: op2 addDenpendency: op1 即 任务2依赖任务1,任务2会等任务1完成
将所有操作都加入到队列中 addOperations:@[op1,op2,op3] waitUntilFinished:
withUntilFinished这个参数类似于GCD中的dispatch_group_wait(g , DISPATCH_TIME_FOREVER) 是否等待所有操作完成
但是注意: 不要指定循环依赖,虽然不会死锁,但是队列会不工作!!!以前的Xcode版本会死锁.
指定依赖关系是可以跨队列进行的. 将op1,op2添加到队列上,将op3添加到主队列上,一样按照指定的依赖关系执行.
NSOperation & GCD的对比
GCD iOS4.0之后推出的针对多核处理器并做了优化的并发技术,是C语言的
- 将"任务"添加到"队列(串行/并发/全局/主队列)",并且指定执行任务的函数(同步/异步)
- 线程间的通讯 dispatch_get_main_queue()
- 提供了一些NSOperation不具备的功能
- 一次性执行
- 延迟操作
- 在NSOperation中也可实现,相对来说比较麻烦
NSOperation是在iOS2.0之后推出的,苹果推出GCD之后,对NSOperation底层重写了一遍
- 将"操作"(异步执行的任务)添加到队列(并发队列),就会立即异步执行
- 线程间的通讯 mainQueue
- 提供了一些GCD实现比较困难的功能
- 最大线程并发数
- 队列的暂停/继续
- 取消所有操作
- 指定操作之间的依赖关系 (GCD可以用同步任务来实现)