iOS开发 - 多线程实现方案之GCD篇
GCD概念
GCD为Grand Central Dispatch的缩写,纯c语言编写,是Apple开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。详细见百度百科
GCD优点
- GCD是苹果公司为多核的并行运算提出的解决方案
- GCD会自动利用更多的CPU内核(比如双核、四核)
- GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
GCD核心
- 任务:执行什么操作
- 队列:用来存放任务
将任务添加到队列中,GCD会自动将队列中的任务取出,放到对应的线程中执行
注意:任务的取出遵循队列的FIFO原则:先进先出,后进后出
GCD执行任务的两种方式比较
1.同步函数:
dispatch_sync(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
只能在当前线程执行任务,不具备开启新线程的能力
2.异步函数:
dispatch_async(<#dispatch_queue_t _Nonnull queue#>, <#^(void)block#>)
在新的线程中执行任务,具备开启新线程的能力
GCD队列的两大类型
1.并发队列(Concurrent Dispatch Queue):
可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_async)函数下才有效
//1.创建并发队列
/**
第一个参数:c语言字符串,标签
第二个参数:队列的类型
DISPATCH_QUEUE_CONCURRENT 并发
DISPATCH_QUEUE_SERIAL 串行
*/
dispatch_queue_t creatQueue = dispatch_queue_create("funky", DISPATCH_QUEUE_CONCURRENT); //2.获取全局的并发队列
/**
第一个参数:优先级 选择默认的优先级
第二个参数:留给以后用的 暂时传0
*/
dispatch_queue_t getQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, );
获得并发队列的方式
2.串行队列(Serial Dispatch Queue):
让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
//1.创建串行队列
dispatch_queue_t creatQueue = dispatch_queue_create("funky", DISPATCH_QUEUE_SERIAL);
//dispatch_release(creatQueue); // 非ARC需要释放手动创建的队列 //2.获取全局的串行队列主队列 主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放到主线程中执行
dispatch_queue_t getQueue = dispatch_get_main_queue();
获得串行队列的方式
GCD的几种执行任务的方式
//异步函数 + 并发队列 :会开启多条线程,队列中的任务是并发(同时)执行,开启的线程条数是由系统内部决定的
- (void)asyncConcurrent { dispatch_queue_t queue = dispatch_queue_create("funky", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
//执行任务
}); } //异步函数 + 串行队列 :会开启一条线程,队列中的任务是串行(一条执行完在执行下一个)
- (void)asyncSerial { dispatch_queue_t queue = dispatch_queue_create("funky", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
//执行任务
}); } //同步函数 + 并发队列 :不会开启线程,任务是串行执行的
- (void)syncConcurrent { dispatch_queue_t queue = dispatch_queue_create("funky", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
//执行任务
}); } //同步函数 + 串行队列 :不会开启线程,任务是串行执行的
- (void)syncSerial { dispatch_queue_t queue = dispatch_queue_create("funky", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
//执行任务
}); } //异步函数 + 主队列 :不会开启线程,在主线程中,任务是串行执行的
- (void)asyncMain { dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
//执行任务
}); } //同步函数 + 主队列 :死锁
- (void)syncMain { /**
分析:若在主线程中执行此方法,首先获得到主队列,然后发现是同步函数,(封装任务,把任务添加到队列中)
队列安排主线程来执行任务,但当前主线程在等待方法执行完毕,这样就会形成死锁 主队列特点:如果主队列发现当前主线程有任务在执行,那么主队列会暂停调用队列中的任务,直到线程空闲为止 如果此方法在子线程中调用,则不会形成死锁
*/ dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
//执行任务
}); }
GCD线程间的通信
//创建子线程下载图片
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{ NSURL *url = [NSURL URLWithString:@"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1489947239211&di=712ed19abb4549e3752acb70fbecc29e&imgtype=0&src=http%3A%2F%2Fe.hiphotos.baidu.com%2Fimage%2Fpic%2Fitem%2F314e251f95cad1c8037ed8c97b3e6709c83d5112.jpg"]; NSData *data = [NSData dataWithContentsOfURL:url]; UIImage *image = [UIImage imageWithData:data]; //在主线程中更新UI
dispatch_async(dispatch_get_main_queue(), ^{ self.imageView.image = image; }); });
下载网络图片
GCD的常见用法
1.延迟执行
//延迟执行方法一
/**
说明:此方法在子线程中,并不会调用SEL方法,原因是afterDelay方式是使用当前线程的定时器在一定时间后调用SEL,而子线程中默认是没有定时器的
解决:1、开启线程的定时器 [[NSRunLoop currentRunLoop] run];
2、使用dispatch_after来执行定时任务
*/
[self performSelector:@selector(test) withObject:nil afterDelay:2.0]; //延迟执行方法二
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:NO]; //延迟执行方法三
//参数一:DISPATCH_TIME_NOW 从现在开始计算时间
//参数二:delayInSeconds 延迟的时间 GCD时间单位:纳秒
//参数三:队列 (可以控制延迟执行在什么线程下执行)
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), queue, ^{
//执行内容
});
延迟执行的几种方式
2.一次性代码(单例中的使用)
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ // 只执行1次的代码(这里面默认是线程安全的)
// 整个APP生命周期中只会执行一次
});
3.队列组的运用场景:分别异步执行2个耗时的操作,等2个异步操作都执行完毕后,再回到主线程执行操作
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{ // 执行1个耗时的异步操作 }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, ), ^{ // 执行1个耗时的异步操作 }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 等前面的异步操作都执行完毕后,回到主线程... });
队列组
4.栅栏函数
//栅栏函数不能使用全局并发队列
//dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t queue = dispatch_queue_create("funky", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{
//任务1
NSLog(@"download1--------%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
//任务2
NSLog(@"download2--------%@",[NSThread currentThread]);
}); //栅栏函数 : 可以让任务1和任务2执行完毕以后在执行栅栏函数以后的其他任务
dispatch_barrier_async(queue, ^{
NSLog(@"++++++++++ 这就是个栅栏 ++++++++");
}); dispatch_async(queue, ^{
NSLog(@"download3--------%@",[NSThread currentThread]);
});
5.快速迭代(遍历)
//普通遍历方式
for (int i = ; i < ; i++) {
NSLog(@"%d------%@",i,[NSThread currentThread]);
} /**
参数1:遍历的次数
参数2:队列(并发队列)
参数3:index 索引
*/ //开子线程和主线程一起完成遍历任务,任务的遍历是并发的
dispatch_apply(, dispatch_get_global_queue(, ), ^(size_t index) {
NSLog(@"%zu------%@",index,[NSThread currentThread]);
});
遍历
6.用函数的方式
- (void)viewDidLoad {
[super viewDidLoad]; //用函数的方式来封装任务
/**
参数一:队列
参数二:参数
参数三:要调用的函数
*/
dispatch_async_f(dispatch_get_global_queue(, ), NULL, task); } void task(void *param){
//执行耗时操作...
}
dispatch_async_f 简单使用
7.几种定时器的使用
-(void)addTimer1 { //NSTimer //方式1
/*
参数一:触发时间,单位秒
参数二:定时起触发对象
参数三:定时器响应方法
参数四:用户信息
参数五:是否重复执行,YES 每个指定的时间重复执行,NO 只执行一次
*/
//会自动将创建的定时器以默认Mode添加到当前线程runloop中,无需手动添加
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:YES]; //立即执行
[timer fire]; //销毁定时器(销毁后不能重新开启)
[timer invalidate]; //关闭定时器
[timer setFireDate:[NSDate distantFuture]]; //开启定时器
[timer setFireDate:[NSDate distantPast]]; //方式2
NSTimer *timer2 = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:YES];
// 将定时器添加到runloop中,否则定时器不会启动
[[NSRunLoop mainRunLoop] addTimer:timer2 forMode:NSRunLoopCommonModes]; }
NSTimer
-(void)addTimer2 { // 创建displayLink
/*
当把CADisplayLink对象add到runloop中后,selector就能被周期性调用,类似于重复的NSTimer被启动了;执行invalidate操作时,CADisplayLink对象就会从runloop中移除,selector调用也随即停止,类似于NSTimer的invalidate方法 CADisplayLink是一个和屏幕刷新率同步的定时器类。CADisplayLink以特定模式注册到runloop后,每当屏幕显示内容刷新结束的时候,runloop就会向CADisplayLink指定的target发送一次指定的selector消息,CADisplayLink类对应的selector就会被调用一次,所以可以使用CADisplayLink做一些和屏幕操作相关的操作。 重要属性: frameInterval : NSInteger类型的值,用来设置间隔多少帧调用一次selector方法,默认值是1,即每帧都调用一次。 duration : readOnly的CFTimeInterval值,表示两次屏幕刷新之间的时间间隔。需要注意的是,该属性在target的selector被首次调用以后才会被赋值。selector的调用间隔时间计算方式是:调用间隔时间 = duration × frameInterval。
*/
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(test)]; // 将创建的displaylink添加到runloop中,否则定时器不会执行
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; //关闭定时器
displayLink.paused = YES; //开启定时器
displayLink.paused = NO; // 销毁定时器
[displayLink invalidate];
displayLink = nil; }
CADisplayLink
@property (nonatomic,strong) dispatch_source_t timer; -(void)addTimer3 { //GCD定时器不会受RunLoop的影响,并且是绝对精准的 //1.创建GCD中的定时器
/*
参数一:source的类型 DISPATCH_SOURCE_TYPE_TIMER 表示定时器
参数二:描述信息
参数三:更详细的描述信息
参数四:队列,决定GCD定时器中的任务在哪个线程中执行
*/
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, , , dispatch_get_global_queue(, )); //2.设置定时器(起始时间|间隔时间|精准度)
/*
参数一:定时器对象
参数二:起始时间,DISPATCH_TIME_NOW 从现在开始计时
参数三:间隔时间 2.0 GCD时间单位是 纳秒
参数四:精准度 绝对精准0
*/
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, * NSEC_PER_SEC); //3.设置定时器执行的任务
dispatch_source_set_event_handler(timer, ^{
NSLog(@"--------%@",[NSThread currentThread]);
}); //4.启动执行
dispatch_resume(timer); //timer 是一个局部变量,2s之后定时器变量可能会被释放了,所以定时器不工作,为了保证不被释放
self.timer = timer; /*
//暂停定时器
dispatch_suspend(self.timer);
//开启定时器
dispatch_resume(self.timer);
//销毁定时器
dispatch_cancel(self.timer);
*/ }
GCD定时器
参考文章: http://www.cnblogs.com/wendingding/p/3806821.html
iOS开发 - 多线程实现方案之GCD篇的更多相关文章
- iOS开发 - 多线程实现方案之Pthread篇
pthread基础 pthread是POSIX thread的简写,一套通用的多线程API,适用于Unix.Linux.Windows等系统,跨平台.可移植,使用难度大,C语言框架,线程生命周期由程序 ...
- iOS开发 - 多线程实现方案之NSOperation篇
NSOperation简介 1.实现多线程编程步骤: 配合使用NSOperation和NSOperationQueue实现多线程编程,我们不用考虑线程的生命周期.同步.加锁等问题,如下: 先将需要执行 ...
- iOS开发 - 多线程实现方案之NSThread篇
NSThread API //类方法:创建一个线程 + (void)detachNewThreadWithBlock:(void (^)(void))block API_AVAILABLE(macos ...
- iOS开发多线程--技术方案
pthread 实现多线程操作 代码实现: void * run(void *param) { for (NSInteger i = 0; i < 1000; i++) { ...
- iOS 开发多线程篇—GCD的常见用法
iOS开发多线程篇—GCD的常见用法 一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) ...
- iOS开发多线程篇—GCD介绍
iOS开发多线程篇—GCD介绍 一.简单介绍 1.什么是GCD? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD的优势 G ...
- iOS开发多线程篇—GCD的基本使用
iOS开发多线程篇—GCD的基本使用 一.主队列介绍 主队列:是和主线程相关联的队列,主队列是GCD自带的一种特殊的串行队列,放在主队列中得任务,都会放到主线程中执行. 提示:如果把任务放到主队列中进 ...
- iOS开发多线程篇—GCD的常见用法
iOS开发多线程篇—GCD的常见用法 一.延迟执行 1.介绍 iOS常见的延时执行有2种方式 (1)调用NSObject的方法 [self performSelector:@selector(run) ...
- iOS开发多线程篇—GCD简介
iOS开发多线程篇—GCD介绍 一.简单介绍 1.什么是GCD? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD的优势 G ...
随机推荐
- 新版的Spring4X怎样下载
点击下载 <a href="http://download.csdn.net/detail/zhaoqingkaitt/7733719">点击免费下载</a> ...
- 如何在时间复杂度为O(n)空间复杂度为O(1)的情况下完成链表的逆置
问题如题目,首先分析,链表的反转的空间复杂度如果为常数级,那么不可能完成从堆中申请数据来完成链表的反转工作,所以问题就转化为了如何将原链表修改/拆解为逆置的链表: 函数形式假定如下 void Inv ...
- NYOJ-37 回文字符串 —— LCS变形
题目链接:http://acm.nyist.net/JudgeOnline/problem.php?pid=37 题解: 一开始想从两边向中间添加字符,发现这样不是最优的.因为加入字符之后,这些原本存 ...
- runtime之实现对象序列化
/* iOS序列化,将对象转成二进制,保存到本地 */ 定义一个对象,让它实现NSCoding协议,保证对象的编码和解码,person有三个属性 @interface Person : NSObjec ...
- Object.prototype.constructor
Returns a reference to the Object function that created the instance's prototype. 注意这个属性的值是函数本省的引用,而 ...
- hdu-5742 It's All In The Mind(数学)
题目链接: It's All In The Mind Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (J ...
- (QA-LSTM)自然语言处理:智能问答 IBM 保险QA QA-LSTM 实现笔记.md
train集: 包含若干条与保险相关的问题,每一组问题对为一行,示意如下: 可分为四项,第三项为问题,第四项为答案: 1.build_vocab 统计训练集中出现的词,返回结果如下(一个包含3085个 ...
- Keras 可视化 model
参考:https://keras.io/visualization/ error解决参考:http://blog.csdn.net/wangjian1204/article/details/50346 ...
- [SDOI2012]任务安排
[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=2726 [算法] 此题与POJ1180非常相似 但是 , 此题中的t值可能为负 , 这 ...
- 【HDU5512】 2015沈阳赛区D题 规律题(GCD)
第一篇博客,就从一个比较简单的题目入手吧! 题目: [HDU5512] 题意: 有n个塔,编号为1~n, 编号为a,b的塔已经维修好,此外其他的塔都需要维修.塔的维修是有顺序的,每次只能维修编号为k ...