iOS多线程的三种方法
前言
在多线程简介中,我已经说明过了,为了提高界面的流畅度以及用户体验。我们务必要把耗时的操作放到别的线程中去执行,千万不要阻塞主线程。
iOS中有以下3种多线程编程方法:
- NSThread
- Grand Centeral Dispatch(GCD)
- NSOperation和NSOperationQueue
1.NSThread
这是最轻量级的多线程的方法,使用起来最直观的多线程编程方法。但是因为需要自己管理线程的生命周期,线程同步。经常使用NSThread进行调试,在实际项目中不推荐使用。
//获取当前线程
NSThread *current = [NSThread currentThread];
//获取主线程
NSThread *main = [NSThread mainThread];
NSLog(@"当前线程 --- %@",current);
NSLog(@"主线程 --- %@",main);
控制台输出结果:
2015-11-22 22:30:29.572 多线程demo[1289:2925847] 当前线程 --- <NSThread: 0x7fc0e1401dc0>{number = 1, name = main}
2015-11-22 22:30:29.572 多线程demo[1289:2925847] 主线程 --- <NSThread: 0x7fc0e1401dc0>{number = 1, name = main}
从结果我们看出当前的线程就是主线程,number相当于线程的id,name是线程的名称,主线程的number就是1
阻塞线程:
//阻塞线程3秒
[NSThread sleepForTimeInterval:3];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
2.GCD(Grand Central Dispatch)
GCD是基于C语言底层API实现的一套多线程并发机制,非常的灵活方便,在实际的开发中使用很广泛。
简单来说CGD就是把操作放在队列中去执行。
你只需定义好操作和队列就可以了,不需要直接控制线程的创建和销毁,线程的生命周期由队列来管理。
队列:负责操作的调度和执行,有先进先出(FIFO)的特点。也就是说先加入队列的操作先执行,后加入的后执行。
队列有两种:
串行队列:
队列中的操作只会按顺序执行,你可以想象成单窗口排队。
并行队列:
队列中的操作可能会并发执行,这取决与操作的类型,你可以想象成多窗口排队。
//创建串行队列
dispatch_queue_t q = dispatch_queue_create("my_serial_queue", DISPATCH_QUEUE_SERIAL);
//创建并行队列
dispatch_queue_t q = dispatch_queue_create("my_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
my_serial_queue和my_concurrent_queue是队列的名字标签,为了与其他的队列区分,在一个项目里面必须是唯一的。
DISPATCH_QUEUE_SERIAL表示串行队列
DISPATCH_QUEUE_CONCURRENT表示并行队列
操作同样也分两种类型:
同步操作:只会按顺序执行,执行顺序是确定的。
异步操作:在串行队列中执行顺序确定,在并行队列中执行顺序不确定
使用block来定义操作要执行的代码,q是已经定义好的,操作要加入的队列
//定义同步操作
dispatch_sync(q, ^{
//要执行的代码
});
//定义异步操作
dispatch_async(q, ^{
//要执行的代码
});
下面我们看一下同步,异步操作加入到串行和并行队列里面,执行的顺序和特点:
1.同步操作不管加入到何种队列,只会在主线程按顺序执行
dispatch_queue_t q_serial = dispatch_queue_create("my_serial_queue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t q_concurrent = dispatch_queue_create("my_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 5; ++i) {
dispatch_sync(q_serial, ^{
NSLog(@"串行队列里的同步任务 %@ %d", [NSThread currentThread], i);
});
}
for (int i = 0; i < 5; ++i) {
dispatch_sync(q_concurrent, ^{
NSLog(@"并行队列里的同步任务 %@ %d", [NSThread currentThread], i);
});
}
下面是控制台输出结果:
2015-11-23 00:40:36.862 01.GCD演练[1952:3613752] 串行队列里的同步任务 <NSThread: 0x7ff833505450>{number = 1, name = main} 0
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 串行队列里的同步任务 <NSThread: 0x7ff833505450>{number = 1, name = main} 1
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 串行队列里的同步任务 <NSThread: 0x7ff833505450>{number = 1, name = main} 2
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 串行队列里的同步任务 <NSThread: 0x7ff833505450>{number = 1, name = main} 3
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 串行队列里的同步任务 <NSThread: 0x7ff833505450>{number = 1, name = main} 4
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 并行队列里的同步任务 <NSThread: 0x7ff833505450>{number = 1, name = main} 0
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 并行队列里的同步任务 <NSThread: 0x7ff833505450>{number = 1, name = main} 1
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 并行队列里的同步任务 <NSThread: 0x7ff833505450>{number = 1, name = main} 2
2015-11-23 00:40:36.864 01.GCD演练[1952:3613752] 并行队列里的同步任务 <NSThread: 0x7ff833505450>{number = 1, name = main} 3
2015-11-23 00:40:36.864 01.GCD演练[1952:3613752] 并行队列里的同步任务 <NSThread: 0x7ff833505450>{number = 1, name = main} 4
2.异步操作只在非主线程的线程执行,在串行队列中异步操作会在新建的线程中按顺序执行。
dispatch_queue_t q_serial = dispatch_queue_create("my_serial_queue", DISPATCH_QUEUE_SERIAL);
for(int i = 0; i < 5; ++i){
dispatch_async(q_serial, ^{
NSLog(@"串行队列 -- 异步任务 %@ %d", [NSThread currentThread], i);
});
}
因为是异步操作,所以会新建一个线程。又因为加入到串行队列中,所以所有的操作只会按顺序执行。
2015-11-23 01:03:22.372 01.GCD演练[2081:3627139] 串行队列 -- 异步任务 <NSThread: 0x7fb593d42f50>{number = 2, name = (null)} 0
2015-11-23 01:03:23.373 01.GCD演练[2081:3627139] 串行队列 -- 异步任务 <NSThread: 0x7fb593d42f50>{number = 2, name = (null)} 1
2015-11-23 01:03:24.374 01.GCD演练[2081:3627139] 串行队列 -- 异步任务 <NSThread: 0x7fb593d42f50>{number = 2, name = (null)} 2
2015-11-23 01:03:25.375 01.GCD演练[2081:3627139] 串行队列 -- 异步任务 <NSThread: 0x7fb593d42f50>{number = 2, name = (null)} 3
2015-11-23 01:03:26.376 01.GCD演练[2081:3627139] 串行队列 -- 异步任务 <NSThread: 0x7fb593d42f50>{number = 2, name = (null)} 4
3.异步操作,并行队列
dispatch_queue_t q_concurrent = dispatch_queue_create("my_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
for(int i = 0; i < 5; ++i){
dispatch_async(q_concurrent, ^{
NSLog(@"并行队列 -- 异步任务 %@ %d", [NSThread currentThread], i);
});
}
理论上并行队列会给每一个异步操作新建线程,然后让所有的任务并发执行。但是实际上系统能创建的线程数量是有限的,当创建的线程达到最大线程数以后,后面的异步操作就需要等待前面的操作执行完毕才能得到执行。哪个线程操作执行完毕,就把等待的异步任务安排到哪个线程。直到所有的操作执行完毕。
你可以把上述代码的循环次数改成5000就可以观察到此现象。
2015-11-23 01:14:15.282 01.GCD演练[2165:3634728] 并行队列 -- 异步任务 <NSThread: 0x7fb3b841b0a0>{number = 4, name = (null)} 3
2015-11-23 01:14:15.282 01.GCD演练[2165:3634724] 并行队列 -- 异步任务 <NSThread: 0x7fb3b8514da0>{number = 3, name = (null)} 0
2015-11-23 01:14:15.282 01.GCD演练[2165:3634726] 并行队列 -- 异步任务 <NSThread: 0x7fb3b8604db0>{number = 5, name = (null)} 2
2015-11-23 01:14:15.282 01.GCD演练[2165:3634725] 并行队列 -- 异步任务 <NSThread: 0x7fb3b86119d0>{number = 2, name = (null)} 1
2015-11-23 01:14:15.285 01.GCD演练[2165:3634729] 并行队列 -- 异步任务 <NSThread: 0x7fb3b87011f0>{number = 6, name = (null)} 4
3.NSOperation & NSOperationQueue
虽然GCD的功能已经很强大了,但是它使用的API依然是C语言的。在某些时候,在面向对象的objective-c中使用起来非常的不方便和不安全。
所以苹果公司把GCD中的操作抽象成NSOperation对象,把队列抽象成NSOperationQueue对象。

抽象为NSOperation & NSOperationQueue以后的好处有一下几点:
- 代码风格统一了,我们不用在面向对象的objective-C中写面对过程的C语言代码了。
- 我们知道在GCD中操作的执行代码都是写在匿名的block里面,那么我们很难做到给操作设置依赖关系以及取消操作。这些功能都已经封装到NSOperation对象里面了。^-^
- NSOperationQueue对象比GCD中队列更加的强大和灵活,比如:设置并发操作数量,取消队列中所有操作。
NSOperation分为NSInvocationOperation和NSBlockOperation
NSInvocationOperation的使用
//首先定义一个NSOperationQueue对象
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction:) object:@"这里可以穿参数"];
[queue addOperation:op];//把操作加入队列中即开始执行
- (void)operationAction:(id)obj
{
NSLog(@"%@ - obj : %@", [NSThread currentThread], obj);
}
输出为:
2015-11-23 02:55:19.067 多线程demo[2604:3686934] <NSThread: 0x7f9dfa443510>{number = 2, name = (null)} - obj : 这里可以穿参数
NSBlockOperation的使用
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
[self operationAction:@"这是NSBlockOperation"];
}];
[queue addOperation:op];
输出为:
2015-11-23 02:56:11.812 多线程demo[2617:3687872] <NSThread: 0x7fa983f10a50>{number = 2, name = (null)} - obj : 这是NSBlockOperation
设置依赖关系(执行顺序)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction:) object:@"op1"];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction:) object:@"op2"];
//op2在op1之后执行
[op2 addDependency:op1];//这里需要注意,一定要在addOperation之前设置依赖关系
[queue addOperation:op1];
[queue addOperation:op2];
输出为:
2015-11-23 02:57:40.283 多线程demo[2661:3689737] <NSThread: 0x7fb663e132d0>{number = 2, name = (null)} - obj : op1
2015-11-23 02:57:40.284 多线程demo[2661:3689737] <NSThread: 0x7fb663e132d0>{number = 2, name = (null)} - obj : op2
没有设置依赖关系的输出:
2015-11-23 03:00:45.939 多线程demo[2709:3692307] <NSThread: 0x7fe951d0d8a0>{number = 2, name = (null)} - obj : op2
2015-11-23 03:00:45.939 多线程demo[2709:3692308] <NSThread: 0x7fe951c24720>{number = 3, name = (null)} - obj : op1
到这里你应该发现了,在NSOperation & NSOperationQueue中,我们不需要再像GCD那样定义操作的类型和队列的类型和控制操作的执行顺序了,你只需要直接设定操作的执行顺序就可以了。
iOS多线程的三种方法的更多相关文章
- iOS 拨打电话三种方法
小弟查了很多地方的关于iOS程序拨打电话,大都不全,今天我总结了三种方法,各有不同,拿来给大家分享,希望给大家有所帮助1,这种方法,拨打完电话回不到原来的应用,会停留在通讯录里,而且是直接拨打,不弹出 ...
- Java基础—实现多线程的三种方法
Java虚拟机(JVM,是运行所有Java程序的抽象计算机,是Java语言的运行环境)允许应用程序并发地运行多个线程.在Java语言中,多线程的实现一般有以下三种方法: 1.实现Runnable接口, ...
- js 与ios 交互的三种方法
第一种:IOS拦截url 实现跳转 参考链接:http://www.cnblogs.com/pengyingh/articles/2354381.html IOS9.0 及以上支持 第二种:IOS ...
- Java创建多线程的三种方法
Java多线程实现方式主要有三种:继承Thread类.实现Runnable接口.使用ExecutorService.Callable.Future实现有返回结果的多线程.其中前两种方式线程执行完后都没 ...
- 实现多线程的三种方法:Thread、Runnable和Callable
继承Thread类,重写run()方法 步骤: (1) 定义类继承Thread类 (2) 复写Thread类中的run方法. (3) 调用线程的start方法 (start方法有两种含义:1. 启动多 ...
- Java 创建多线程的三种方法
1. 继承Thread类2. 实现Runnable接口3. 匿名类的方式 注: 启动线程是start()方法,run()并不能启动一个新的线程
- iOS开发进阶-实现多线程的3种方法
相关文章链接: 1.多线程简介 2.实现多线程的3种方法 ......待续 前言 在多线程简介中,我已经说明过了,为了提高界面的流畅度以及用户体验.我们务必要把耗时的操作放到别的线程中去执行,千万不要 ...
- iOS拨打电话(三种方法)
iOS拨打电话(三种方法) 查了很多地方的关于iOS程序拨打电话,大都不全,今天我总结了三种方法,各有不同,拿来给大家分享,希望给大家有所帮助 1,这种方法,拨打完电话回不到原来的应用,会停留在通讯 ...
- java多线程二之线程同步的三种方法
java多线程的难点是在:处理多个线程同步与并发运行时线程间的通信问题.java在处理线程同步时,常用方法有: 1.synchronized关键字. 2.Lock显示加锁. 3.信号量Se ...
随机推荐
- vbs文件小技巧
vbs文件介绍: VBS是基于Visual Basic的脚本语言.VBS的全称是:Microsoft Visual Basic Script Editon.(微软公司可视化BASIC脚本版). 可以新 ...
- wemall app商城源码Android之Native(原生)支付模式一demo
wemall-mobile是基于WeMall的Android app商城,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可定制修改.本文分享Native(原生)支付模式一demo,供技术 ...
- 使用Eclipse/MyEclipse开发Java程序
集成开发环境(IDE)是一类软件 将程序开发环境和程序调试环境集合在一起,提高开发效率 下载eclipse安装包网址:http://www.eclipse.org/downloads/ **MyEcl ...
- jenkins gradle 编译遇到tomcat异常
使用gradle在jenkins 上构建的时候遇到 进行构建的时候报"Could not load Logmanager org.apache.juli.ClassLoaderLogMana ...
- Untiy文档总结(1)-Profiling
这段时间上班了,不是什么大公司,虽说很闲但是不能断了学习,就开始看优化有关的文档,虽说自己英语渣的要死,但也要读下去,逼着自己翻译完了,里面有抄Unity圣典的,但自己是看Unity5.5文档写的,只 ...
- 写入soap消息以及与soap消息通信
1.些了解一下soap消息的结构以及通信方式如下图:
- iOS开发之@Selector参数的传递
比如: NSMutableDictionary *dic = [[NUMutableDictionary alloc]init]; [dic setValue:@”haha” forKey:@”tes ...
- Javascript动态创建 style 节点
有很多提供动态创建 style 节点的方法,但是大多数都仅限于外部的 css 文件.如何能使用程序生成的字符串动态创建 style 节点,我搞了2个小时. 静态外部 css 文件语法: @import ...
- JS中的异步以及事件轮询机制
一.JS为何是单线程的? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊.(在JAVA和c#中的异步 ...
- PMP和PRINCE2应该选择哪个?光环国际项目管理认证
对于项目管理课程的选择,我们不能盲目地做选择,一定要从自身实际出发.从来都没有更好的课程,只有更合适自己的课程. 那么,如何选择合适自己的项目管理课程呢? 让我们从PMP与PRINCE2之间的差异开始 ...