本系列所有开发文档翻译链接地址:iOS7开发-Apple苹果iPhone开发Xcode官方文档翻译PDF下载地址(2013年12月29日更新版)
多线程
技术博客http://www.cnblogs.com/ChenYilong/ 新浪微博http://weibo.com/luohanchenyilong
多线程的应用
• 耗时操作,例如网络图片、视频、歌曲、书籍等资源下载 • 游戏中的声音播放
多线程示意图
• 充分发挥多核处理器的优势,并发(同时执行) 执行任务让系统运行的更快、更流畅
进程与线程概念
• 一个运行的程序就是一个进程或者叫做一个任务
• 一个进程至少包含一个线程,线程是程序的执行流
• iOS程序启动时,在创建一个进程的同时, 会开始运行一个线程,该 线程被称为主线程
• 主线程是其他线程最终的父线程,所有界面的显示操作必须在主线程 进行
• 后台线程无法更新UI界面和响应用户点击事件
• 系统中的每一个进程都有自己独立的虚拟内存空间,而同一个进程中
的多个线程则共用进程的内存空间
• 每创建一个新的线程,都会消耗一定内存和CPU时间
• 当多个线程对同一个资源出现争夺的时候需要注意线程安全问题
多线程的优势与难点
• 优势
– 充分发挥多核处理器优势,将不同线程任务分配给不同的处
理器,真正进入“并行运算”状态
– 将耗时、轮询或者并发需求高等任务分配到其他线程执行, 并由主线程负责统一更新界面会使得应用程序更加流畅,用 户体验更好
– 当硬件处理器的数量增加,程序会运行更快,而无需做任何 调整
• 难点
– 共享资源的“争夺”
– 多线程是为了同步完成多项任务,不是为了提高运行效率, 而是为了通过提高资源使用效率来提高系统的整体性能
多线程使用注意事项
• 线程使用不是无节制的– iOS中的主线程的堆栈大小是1M
– 从第二个线程开始都是512KB
– 这些数值不能通过编译器开关或线程API函数更改
• 只有主线程有直接修改UI的能力
iOS的三种多线程技术
1.
NSThread
每个
NSThread
对象对应一个线程,量级较轻(真正的多线
程)
2.
以下两点是苹果专门开发的“并发”技术,使得程序员可以不再去 关心线程的具体使用问题
.1.
NSOperation/NSOperationQueue
面向对象的线程技术
.2.
GCD —— Grand Central Dispatch
(派发)
是基于
C
语言的框架,
可以充分利用多
核
,是苹果推荐使用的多线程技术
.
以上这三种编程方式从上到下,抽象度层次是从低到高的,
抽象度越高 的使用越简单,也是
Apple
最推荐使用的
。但是就目前而言,
iOS
的开发者, 需要了解三种多线程技术的基本使用过程。因为很多框架技术分别使用 了不同多线程技术。
三种多线程技术的对比
•
NSThread:
–
优点:
NSThread
比其他两个轻量级,
使用简单
–
缺点:
需要自己管理线程的生命周期、线程同步、加锁、睡眠以 及唤醒等
。线程同步
对数据的加锁
会有一定的系统开销
•
NSOperation
:
–
不需要关心线程管理,数据同步的事情,可以把精力放在自己需
要执行的操作上
–
NSOperation
是面向对象的
•
GCD
:
–
Grand Central Dispatch
是
由苹果开发的一个多核编程的解决方案
。
iOS4.0+
才能使用,是替代
NSThread
,
NSOperation
的高效和强大 的技术
//转载请注明出处--本文永久链接:http://www.cnblogs.com/ChenYilong/p/3494799.html
–
GCD
是基于
C
语言的
演练(
1
)
NSObject
的多线程方法
——
准备
.1.
创建一个耗时较长的操作
.2.
创建一个耗时较短的操作
.3.
在界面中心放置两个按钮,分别调用 这两个任务
.4.
使用
[NSThread currentThread]
分别打 印各个任务所在的线程
.5.
运行观察效果
•
提示:
•
NSLog
是一个相当耗时的操作 在应用 程序正式发布前,一定记住需要对应用 中的
NSLog
方法进行处理
•
无论使用哪一种多线程技术,均可以使 用
[NSThread currentThread]
查看当前任 务所在线程
NSObject
的多线程方法
——
后台线程
- (
void
)performSelectorInBackground:(
SEL
)aSelector
withObject:(
id
)arg
•
通常,由于线程管理相对比较繁琐,而很多耗时的任务又无法知道其准 确的完成时间,因此可以使用
performSelectorInBackground
方法 直接新建一个后台线程,并将选择器指定的任务在后台线程执行,而无 需关心具体的
NSThread
对象
•
提示:
–
performSelectorInBackground
方法本身是在主线程中执行的,
而选择器指定的方法是在后台线程中进行的
–
使用
performSelectorInBackground
方法调用的任务可以更新
UI
界面
–
在大型交互式游戏中,通常使用此方法在后台线程播放音效
@autoreleasepool
•
内存管理对于多线程非常重要
•
Objective-C
可以凭借
@autoreleasepool
使用内存资源,并需要时回 收资源
•
每个线程都需要有
@autoreleasepool
,否则可能会出现内存泄漏
NSObject
的多线程方法
——
主线程
- (
void
)performSelectorOnMainThread:(
SEL
)aSelector
withObject:(
id
)arg waitUntilDone:(
BOOL
)wait;
•
如果要更新
UI
界面,可以在后台线程中调用
performSelectorOnMainThread
方法
•
提示
:尽管使用
performSelectorInBackground
方法调用的任务 可以更新
UI
界面,但是在实际开发中,涉及到
UI
界面的更新操作,还 是要使用
performSelectorOnMainThread
方法,以避免不必要的 麻烦
NSObject
的多线程小结
•
开启后台执行任务的方法
-
(
void
)performSelectorInBackground:(
SEL
)aSelector withObject:(
id
)arg
•
在后台线程中通知主线程执行任务的方法
–
(
void
)performSelectorOnMainThread:(
SEL
)aSelector
withObject:(
id
)arg waitUntilDone:(
BOOL
)wait;
•
获取线程信息
[
NSThread
currentThread
];
•
线程休眠
[
NSThread
sleepForTimeInterval
:
1.0f
];
•
特点:
–
使用简单,量级轻
–
不能控制线程的执行顺序
NSThread
•
创建线程方法:
1.
+ (
void
)detachNewThreadSelector:(
SEL
)
selector
toTarget:(
id
)
target
withObject:(
id
)
argument
;
2.
- (
id
)initWithTarget:(
id
)
target
selector: (
SEL
)
selector
object:(
id
)
argument
;
•
参数说明:
–
selector
:
线程执行的方法
,只能有一个参数,不能有返回值
–
target
:
selector
消息发送的对象
–
argument
:传输给
target
的唯一参数,也可以是
nil
NSThread
演练
——
加载图片
•
detachNewThreadSelector
方法会直接启动线程方法
•
initWithTarget
需要调用
start
方法才能够启动线程方 法
NSOperation & NSOperationQueue
•
NSOperation
的两个子类
1.
NSInvocationOperation 2.
NSBlockOperation
•
工作原理:
1.
用
NSOperation
封装要执行的操作
2.
将创建好的
NSOperation
对象放
NSOperationQueue
中
3.
启动
OperationQueue
开始新的线程执行队列中的操作
•
注意事项:
.1.
使用多线程时通常需要控制线程的并发数
,因为线程会消耗系统资源,
同时运行的线程过多,系统会变慢
.
.2.
使用以下方法可以控制并发的线程数量:
-
(
void
)setMaxConcurrentOperationCount:(
NSInteger
)cnt;
//转载请注明出处--本文永久链接:http://www.cnblogs.com/ChenYilong/p/3494799.html
NSOperation
演练
——
加载图片
.1.
不能直接使用
NSOperation
.2.
定义完操作后,
将添加到操作队列中
,即可启动异步操作,否则操 作任务仍然在主线程中执行
.3.
使用
NSBlockOperation
更加简单直接
.4.
使用
setMaxConcurrentOperationCount
可以限制并发操作数
量,降低系统开销
.5.
使用
addDependency
可以建立操作之间的依赖关系,设定操作的执行 顺序
GCD
•
GCD
是基于
C
语言的框架
•
工作原理
:
–
让程序平行排队的特定任务,根据可用的处理资源,
安排它们在
任何可用的处理器上执行任务
–
要执行的任务可以是一个函数或者一个
block
–
底层是通过线程实现的,不过程序员可以不必关注实现的细节
–
GCD
中的
FIFO
队列称为
dispatch queue
,可以保证先进来的任务先 得到执行
–
dispatch_notify
可以实现
监听一组任务是否完成
,完成后得 到通知
•
GCD
队列
:
1.
全局队列:
所有添加到主队列中的任务都是并发执行的
2.
串行队列:
所有添加到串行队列中的任务都是顺序执行的
3.
主队列:
所有添加到主队列中的任务都是在主线程中执行的
获取队列的方法
•
全局队列(可能会开启多条线程)
dispatch_queue_t
queue =
dispatch_get_global_queue
(
DISPATCH_QUEUE_PRIORITY_DEFAULT
,
0
);
•
串行队列(只可能会开启一条线程)
dispatch_queue_t
queue =
dispatch_queue_create
(
"myQueue"
,
DISPATCH_QUEUE_SERIAL
);
•
主队列
dispatch_get_main_queue
();
GCD
任务的执行方式
——
同步
&
异步
•
异步操作
–
dispatch_async
在其他线程执行任务,会开启新的线程
–
异步方法无法确定任务的执行顺序
•
同步操作
–
dispatch_sync
在当前在当前线程执行任务,不开启新的线程
–
同步操作与队列无关
–
同步方法会依次执行,能够决定任务的执行顺序
–
更新界面
UI
时,最好使用同步方法
GCD
演练
——
加载图片
•
GCD
的优点:
–
充分利用多核
–
所有的多线程代码集中在一起,便于维护
–
GCD
中无需使用
@autoreleasepool
–
如果要顺序执行,可以使用
dispatch_sync
同步方法
–
dispatch_async
无法确定任务的执行顺序
单例模型
•
目的
:
–
保证在内存中永远只有类的单个实例
•
建立方法
:
1.
声明一个静态成员变量,记录唯一实例
2.
重写
allocWithZone
方法
allocWithZone
方法是对象分配内存空间时,最终会调用的方法, 重写该方法,保证只会分配一个内存空间
3.
建立
sharedXXX
类方法,便于其他类访问
互斥锁的目的,一次只让一个线程访问资源,从而达到资源的线程安全。
在
iPhone
开发中,通常要尽量避免使用互斥锁!
多线程演练
——
卖票
•
系统预设
–
共有
30
张票可以销售(
开发时可以少一些,专注实现)
–
售票工作由两个线程并发进行
–
没有可出售票据时,线程工作停止
–
两个线程的执行时间不同,模拟售票人员效率不同
–
使用一个多行文本框公告售票进度(主线程更新
UI
)
•
线程工作安排
–
主线程:负责更新
UI
–
线程
1
:模拟第
1
名卖票员
–
线程
2
:模拟第
2
名卖票员
–
两个线程
几乎
同时开始卖票
单线程卖票流程图
多线程卖票示意图
演练准备
——
更新
UI
方法,由主线程调用
// 1.
取出当前的文本
NSMutableString
*str = [
NSMutableString
stringWithString
: [
self
.
textView
text
]];
// 2.
追加文本
[str
appendFormat
:
@"%@\n"
, text];
// 3.
设置文本
[
self
.
textView
setText
:str];
// 4.
选中最末位置,实现自动滚动效果
NSRange
range =
NSMakeRange
(str.
length
-
1
,
1
); [
self
.
textView
scrollRangeToVisible
:range];
//转载请注明出处--本文永久链接:http://www.cnblogs.com/ChenYilong/p/3494799.html
卖票演练
•
资源争夺
–
仅使用单例模式无法解决资源争夺问题
–
使用互斥锁
@synchronized
可以保证多个线程不会使用同一代码块,而且比
NSLock
具有更好的性能
–
为了保证属性安全,被争夺资源的属性应该设置为原子属性
atomic
•
GCD
–
GCD
的多线程更加灵活、方便
–
dispatch_group_notify
可以监听一组任务是否完成
。这个方法很有用,比如
你执行三个下载任务,当三个任务都下载完成后,才通知界面说已经完成
–
如果不需要监听一组任务,可以直接使用
dispatch_async
方法
•
NSOperation
–
更新界面时使用
[[
NSOperationQueue
mainQueue
]
addOperationWithBlock
:
方法
•
NSThread
–
涉及到线程调度问题,日常开发不建议使用
三种多线程技术的流程对比
关于
iOS
多线程使用的建议
•
掌握大纲内容即可,关于多个线程之间的调度问题,待日后熟练后可
以自行学习,目前不建议再继续深入
•
关于多线程必须记住的三个要点
–
只能在主线程中更新
UI
–
共享数据争夺的处理
,互斥锁
@synchronized
(
self
)
和原子属
性
atomic
–
不要使用多种多线程技术去争夺同一个资源
•
使用多线程是为了处理并发操作的。如果有可能,我们不要去做抢资 源的事情 互斥锁的代价相当的昂贵
//转载请注明出处--本文永久链接:http://www.cnblogs.com/ChenYilong/p/3494799.html
https://www.evernote.com/shard/s227/sh/8e2008b3-1d9e-42e6-b193-551e6dbe7440/e39f99e835da159ac641e28679c3c00b