移动开发(IOS) – 多线程
来源:互联网 发布:淘宝权重值 编辑:程序博客网 时间:2024/06/05 18:35
移动开发(IOS) – 多线程
1.概念
1.1.系统中的每一个进程都有自己独立的虚拟内存空间,而同一个进程中的多个线程则共用进程的内存空间。
1.2.每创建一个新的线程,都会消耗一定内存和CPU时间。
1.3.当多个线程对同一个资源出现争夺的时候需要注意线程安全问题。
1.4.多线程的优势:
1.4.1.充分发挥多核处理器优势,将不同线程任务分配给不同的处理器,真正进入“并行运算”状态。
1.4.2.将耗时、轮询或者并发需求高等任务分配到其他线程执行,并由主线程负责统一更新界面会使得应用程序更加流畅,用户体验更好。
1.4.3.当硬件处理器的数量增加,程序会运行更快,而无需做任何调整。
1.5.多线程的难点:
1.5.1.共享资源的“争夺。
1.5.2.多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了通过提高资源使用效率来提高系统的整体性能。
1.6.只有主线程有直接修改UI的能力。
1.7.内存管理对于多线程非常重要。
1.7.1.Objective-C 可以凭借 @autoreleasepool 使用内存资源,并需要时回收资源。
1.7.2.每个线程都需要有 @autoreleasepool ,否则可能会出现内存泄漏。
2.iOS的三种多线程技术
2.1.NSThread 每个 NSThread 对象对应一个线程,量级较轻。
2.1.1.优点:NSThread 比其他两个轻量级,使用简单。
2.1.2.缺点:需要自己管理线程的生命周期、线程同步、加锁、睡眠以及唤醒等。线程同步对数据的加锁会有一定的系统开销。
2.2.NSOperation/NSOperationQueue 面向对象的线程技术。
2.2.1.不需要关心线程管理,数据同步的事情,可以把精力放在自己需要执行的操作上。
2.2.2.NSOperation是面向对象的。
2.3.GCD(Grand Central Dispatch) 是基于 C 语言的框架,可以充分利用多核,是苹果推荐使用的多线程技术。
2.3.1.Grand Central Dispatch 是由苹果开发的一个多核编程的解决方案。iOS4.0+才能使用,是替代 NSThread, NSOperation的高效和强大的技术。
2.3.2.GCD 是基于 C 语言的。
以上这三种编程方式从上到下,抽象度层次是从低到高的,抽象度越高的使用越简单,也是Apple最推荐使用的。
3.NSObject的多线程方法
3.1.通常,由于线程管理相对比较繁琐,而很多耗时的任务又无法知道其准确的完成时间,因此可以使用 performSelectorInBackground 方法直接新建一个后台线程,并将选择器指定的任务在后台线程执行,而无需关心具体的 NSThread 对象:
- (
void
)performSelectorInBackground:(
SEL
)aSelector withObject:(
id
)arg
3.1.1.performSelectorInBackground 方法本身是在主线程中执行的,而选择器指定的方法是在后台线程中进行的。
3.1.2.使用 performSelectorInBackground 方法调用的任务可以更新 UI 界面。
3.2.如果要更新UI界面,可以在后台线程中调用 performSelectorOnMainThread 方法:
- (
void
)performSelectorOnMainThread:(
SEL
)aSelector withObject:(
id
)arg waitUntilDone:(
BOOL
)wait;
尽管使用 performSelectorInBackground 方法调用的任务可以更新UI界面,但是在实际开发中,涉及到 UI 界面的更新操作,还是要使用 performSelectorOnMainThread 方法,以避免不必要的麻烦。
3.3.获取线程信息:
[
NSThread
currentThread];
3.4.线程休眠(仅适用于开发调试时使用):
[
NSThread
sleepForTimeInterval:1.0f];
3.5.特点:使用简单,量级轻;不能控制线程的执行顺序。
4.资源争夺
4.1.仅使用单例模式无法解决资源争夺问题。
4.2.使用同步锁 @synchronized 可以保证多个线程不会使用同一代码块,而且比 NSLock 具有更好的性能。
4.3.为了保证属性安全,被争夺资源的属性应该设置为原子属性 atomic。
5.NSThread
5.1.创建线程方法:
/*
* 参数说明:
* selector:线程执行的方法,只能有一个参数,不能有返回值
* target:selector消息发送的对象
* argument:传输给target的唯一参数,也可以是nil
*/
+ (
void
)detachNewThreadSelector:(
SEL
)selector toTarget:(
id
)target withObject:(
id
)argument;
- (
id
)initWithTarget:(
id
)target selector:(
SEL
)selector object:(
id
)argument;
5.1.1.detachNewThreadSelector 方法会直接启动线程方法。
5.1.2.initWithTarget 需要调用 start 方法才能够启动线程方法。
//类方法
[
NSThread
detachNewThreadSelector:
@selector
(threadSaleTicketWithName:) toTarget:
self
withObject:
@"thread-1"
];
//init方法
NSThread
*
thread
= [[
NSThread
alloc]initWithTarget:
self
selector:
@selector
(threadSaleTicketWithName:) object:
@"thread-2"
];
// 启动线程
[
thread
start];
- (
void
)threadSaleTicketWithName:(
NSString
*)name
{
// 使用 NSThread 时,线程调用的方法千万要使用 @autoreleasepool
@autoreleasepool
{
while
(
YES
) {
@synchronized
(
self
) {
if
([Ticket sharedTicket].tickets > 0) {
[Ticket sharedTicket].tickets--;
NSString
*str = [
NSString
stringWithFormat:
@"剩余票数 %d 线程名称 %@"
, [Ticket sharedTicket].tickets, name];
// 更新UI
[
self
performSelectorOnMainThread:
@selector
(appendContent:) withObject:str waitUntilDone:
YES
];
}
else
{
break
;
}
}
// 模拟休息
if
([name isEqualToString:
@"thread-1"
]) {
[
NSThread
sleepForTimeInterval:1.0f];
}
else
{
[
NSThread
sleepForTimeInterval:0.1f];
}
}
}
}
6.NSOperation & NSOperationQueue
6.1.NSOperation 的两个子类:NSInvocationOperation, NSBlockOperation。
6.2.工作原理:
6.2.1.用 NSOperation 封装要执行的操作。
6.2.2.将创建好的 NSOperation 对象放 NSOperationQueue 中。
6.2.3.启动 OperationQueue 开始新的线程执行队列中的操作。
6.3.注意事项:
6.3.1.使用 NSBlockOperation 更加简单直接。
6.3.2.定义完操作后,将添加到操作队列中,即可启动异步操作,否则操作任务仍然在主线程中执行。
6.3.3.使用多线程时通常需要控制线程的并发数,因为线程会消耗系统资源,同时运行的线程过多,系统会变慢。
6.3.4.使用以下方法可以控制并发的线程数量:
- (
void
)setMaxConcurrentOperationCount:(
NSInteger
)cnt;
6.3.5.使用 addDependency 可以建立操作之间的依赖关系,设定操作的执行顺序。
6.4.更新界面时使用 [[NSOperationQueue mainQueue]addOperationWithBlock: 方法。
//队列可以设置同时并发线程的数量
[
self
.queue setMaxConcurrentOperationCount:2];
NSBlockOperation
*op1 = [
NSBlockOperation
blockOperationWithBlock:^{
NSLog
(
@"下载 %@"
, [
NSThread
currentThread]);
}];
NSBlockOperation
*op2 = [
NSBlockOperation
blockOperationWithBlock:^{
NSLog
(
@"美化 %@"
, [
NSThread
currentThread]);
}];
NSBlockOperation
*op3 = [
NSBlockOperation
blockOperationWithBlock:^{
NSLog
(
@"更新 %@"
, [
NSThread
currentThread]);
//在主线程更新 UI
[[
NSOperationQueue
mainQueue]addOperationWithBlock:^{
NSLog
(
@"更新 UI %@"
, [
NSThread
currentThread]);
}];
}];
// Dependency依赖
// 依赖关系可以多重依赖
// 不要建立循环依赖
[op2 addDependency:op1];
[op3 addDependency:op2];
[
self
.queue addOperation:op3];
[
self
.queue addOperation:op1];
[
self
.queue addOperation:op2];
7.GCD
7.1.GCD 是基于 C 语言的框架。
7.2.工作原理:
7.2.1.让程序平行排队的特定任务,根据可用的处理资源,安排它们在任何可用的处理器上执行任务。
7.2.2.要执行的任务可以是一个函数或者一个 block 。
7.2.3.底层是通过线程实现的,不过程序员可以不必关注实现的细节。
7.2.4.GCD 中的 FIFO 队列称为 dispatch queue,可以保证先进来的任务先得到执行。
7.2.5.dispatch_notify 可以实现监听一组任务是否完成,完成后得到通知。
7.3.GCD 队列:
7.3.1.全局队列:所有添加到主队列中的任务都是并发执行的。(可能会开启多条线程)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
7.3.2.串行队列:所有添加到串行队列中的任务都是顺序执行的。(只可能会开启一条线程)
//创建串行队列,串行队列不能够获取。
//队列名称可以随意,不过不要使用@
dispatch_queue_t queue = dispatch_queue_create(
"myQueue"
, DISPATCH_QUEUE_SERIAL);
7.3.3.主队列:所有添加到主队列中的任务都是在主线程中执行的。
dispatch_get_main_queue();
7.4.GCD任务的执行方式
7.4.1.异步操作:dispatch_async 在其他线程执行任务,会开启新的线程,异步方法无法确定任务的执行顺序。
7.4.2.同步操作:dispatch_sync 在当前在当前线程执行任务,不开启新的线程。同步操作与队列无关,同步方法会依次执行,能够决定任务的执行顺序,更新界面 UI 时,最好使用同步方法。
7.5.GCD 的优点:
7.5.1.充分利用多核。
7.5.2.所有的多线程代码集中在一起,便于维护。
7.5.3.GCD 中无需使用 @autoreleasepool。
// 1. 获取全局队列
//优先级:DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for
(UIImageView *imageView in
self
.imageViewSet) {
// 2. 在全局队列上异步调用方法,加载并更新图像
dispatch_async(queue, ^{
NSLog
(
@"GCD- %@"
, [
NSThread
currentThread]);
NSInteger
num = arc4random_uniform(17) + 1;
NSString
*imageName = [
NSString
stringWithFormat:
@"NatGeo%02d.png"
, num];
// 通常此处的image是从网络上获取的
UIImage *image = [UIImage imageNamed:imageName];
// 3. 在在主线程队列中,调用同步步方法设置UI
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog
(
@"更新图片- %@"
, [
NSThread
currentThread]);
[imageView setImage:image];
});
});
}
// 创建全局队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// GCD中可以将一组相关联的操作,定义到一个群组中
// 定义到群组中之后,当所有线程完成时,可以获得通知
// 1) 定义群组
dispatch_group_t group = dispatch_group_create();
// 2) 定义群组的异步任务
dispatch_group_async(group, queue, ^{
[
self
gcdSaleTicketWithName:
@"gcd-1"
];
});
dispatch_group_async(group, queue, ^{
[
self
gcdSaleTicketWithName:
@"gcd-2"
];
});
dispatch_group_async(group, queue, ^{
[
self
gcdSaleTicketWithName:
@"gcd-3"
];
});
// 3) 群组任务完成通知
dispatch_group_notify(group, queue, ^{
NSLog
(
@"卖完了"
);
});
- (
void
)gcdSaleTicketWithName:(
NSString
*)name
{
while
(
YES
) {
// 同步锁synchronized要锁的范围,对被抢夺资源修改/读取的代码部分
@synchronized
(
self
) {
if
([Ticket sharedTicket].tickets > 0) {
[Ticket sharedTicket].tickets--;
// 提示内容
NSString
*str = [
NSString
stringWithFormat:
@"剩余票数 %d, 线程名称 %@"
, [Ticket sharedTicket].tickets, name];
// 更新界面
dispatch_sync(dispatch_get_main_queue(), ^{
[
self
appendContent:str];
});
}
else
{
break
;
}
}
// 模拟线程休眠
if
([name isEqualToString:
@"gcd-1"
]) {
[
NSThread
sleepForTimeInterval:1.0f];
}
else
{
[
NSThread
sleepForTimeInterval:0.2f];
}
}
}
- 移动开发(IOS) – 多线程
- iOS移动开发多线程—GCD介绍
- 移动开发(IOS) – 动画
- 移动开发(IOS) –传感器
- 移动开发(IOS) – iOS系统架构
- iOS-----IOS多线程开发
- 移动开发(IOS) – UIKit框架
- iOS移动开发周报
- iOS移动开发周报
- IOS多线程开发
- IOS多线程开发
- iOS多线程开发 NSThread
- iOS多线程开发指南
- IOS开发 多线程 一
- IOS多线程开发
- ios 开发中的多线程
- ios 开发中的多线程
- iOS多线程开发 NSThread
- 易信每月送90分钟/月通话
- 深入HRESULT与Windows Error Codes的区别详解
- jstl标签库例子解析
- BCG属性列表
- 安卓第一课:通过一个简单的MP3播放器来认识安卓开发【下】
- 移动开发(IOS) – 多线程
- 移动开发(IOS) – 动画
- TCP/IP详解--TCP首部的TimeStamp时间戳选项
- BCG圆盘菜单
- cocos2d_x_07_游戏_别踩白块儿
- 第十七章 17.2.1节练习 &17.2.2节练习
- 移动开发(IOS) – Quartz 2D绘图
- 数据挖掘算法
- 每周算法练习——最近对问题