GCD使用(多线程004)
来源:互联网 发布:php分割字符串 编辑:程序博客网 时间:2024/06/06 07:39
1. RunLoop介绍
- Runloop被称为消息循环或事件循环
- 每个线程里,都有一个消息循环
- 默认情况下,主线程开启消息循环,子线程不开启
目的
- 保证程序不退出
- 负责处理输入事件
- 如果没有事件处理,会让程序进行休眠
消息类型(事件类型)
- Input Sources(输入源)
Input for sources such as mouse and keyboard events from the window system, NSPort objects, and NSConnection objects.
包括键盘鼠标事件,NSPort,NSConnection,等。
- Timer Sources(定时源)
An NSRunLoop object also processes NSTimer events.
就是定时器NSTimer
常用的循环模式
- NSDefaultRunLoopMode
The mode to deal with input sources other than NSConnection objects. This is the most commonly used run-loop mode. Available in iOS 2.0 and later.
- NSRunLoopCommonModes
Objects added to a run loop using this value as the mode are monitored by all run loop modes that have been declared as a member of the set of “common" modes; see the description of CFRunLoopAddCommonMode for details.
如何使用
- 创建消息
- 把消息加入到消息循环中,并指定循环的模式
加入的2种方法
[self performSelector:@selector(demo2) onThread:thread withObject:nil waitUntilDone:NO];[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
只有当消息的循环模式和当前线程中消息循环的模式像符合,消息才能被运行
子线程中的消息循环
子线程中默认不开启消息循环
开启方式
Run方法
[[NSRunLoop currentRunLoop] run];
缺点:开启之后无法关闭,并且在他之后的代码都不会被运行,因为他是一个死循环
runUntilDate
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];
- 苹果推荐方法
BOOL shouldKeepRunning = YES; // global NSRunLoop *theRL = [NSRunLoop currentRunLoop]; while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
问题演示
- 线程没有执行方法,线程立马销毁
NSThread *thread = [[NSThread alloc] init];[thread start];[self performSelector:@selector(demo2) onThread:thread withObject:nil waitUntilDone:NO];
- 线程指定方法,但是没有开启消息循环
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil]; [thread start]; [self performSelector:@selector(demo2) onThread:thread withObject:nil waitUntilDone:NO]; .......................... - (void)demo { NSLog(@"over");}
2. GCD简介
- 全称是Grand Central Dispatch
- 纯C语言,提供了非常多强大的函数
优点
- GCD是苹果公司为多核的并行运算提出的解决方案
- GCD会自动利用更多的CPU内核(比如双核、四核)
- GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
核心组成部分
任务&队列
任务:要干什么事情
队列:用来存放任务
如何做
- 确定要做的事情
- 把任务加到队列里
- 没了
GCD会自动将任务从队列里面取出,放到对应的贤臣各种去执行
任务取出的原则是先进先出FIFO(First in first out)
举例
//任务dispatch_block_t block;//队列dispatch_queue_t queue;//把任务放到队列里dispatch_async(queue, block);
3. 队列执行任务的方式
同步执行任务
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);queue:队列block:任务
异步执行任务
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
区别
同步:在当前线程上执行 异步:在其他线程上执行
4. 队列的种类
并发队列(Concurrent Dispatch Queue)
- 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
- 并发功能只有在异步(dispatch_async)函数下才有效
dispatch_queue_t queue = dispatch_queue_create("CONCURRENT", DISPATCH_QUEUE_CONCURRENT);
串行队列(Serial Dispatch Queue)
- 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
dispatch_queue_t queue = dispatch_queue_create("SERIAL", DISPATCH_QUEUE_SERIAL);
5. 同步、异步、并发、串行的基本概念
同步和异步决定了要不要开启新的线程
- 同步:在
当前
线程中执行任务,不具备
开启新线程的能力 - 异步:在
新的
线程中执行任务,具备
开启新线程的能力
并发和串行决定了任务的执行方式
- 并发:多个任务并发
(同时)
执行 - 串行:一个任务执行完毕后,再执行
下一个
任务
6. 串行队列的执行
串行队列的同步执行
- 不开线程,同步执行(在当前线程执行)
#define DISPATCH_QUEUE_SERIAL NULL //串行队列 //dispatch_queue_t q = dispatch_queue_create("test", NULL); dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL); for (int i = 0; i < 10; i++) { //同步执行 dispatch_sync(q, ^{ NSLog(@"%@ -- %d",[NSThread currentThread],i); }); }
串行队列的异步执行
- 开一个线程,顺序执行
//只有一个线程,因为是串行队列,只有一个线程就可以按顺序执行队列中的所有任务 dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL); for (int i = 0; i < 10; i++) { //异步执行 dispatch_async(q, ^{ NSLog(@"%@ -- %d",[NSThread currentThread],i); }); }
7. 并行队列的执行
并行队列,异步执行
- 开多个线程,异步执行,每次开启多少个线程是
不固定
的(线程数,不由我们控制),线程数是由gcd来决定的
dispatch_queue_t q = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT); for (int i = 0; i < 10; i++) { //异步执行 dispatch_async(q, ^{ NSLog(@"%@ -- %d",[NSThread currentThread],i); }); }
并行队列,同步执行
- 不开线程,顺序执行,与
串行队列的同步执行
一模一样
8. 系统提供的队列
iOS已经为我们准备好了2个常用队列主队列
列和全局并发队列
,开发中用的很多
主队列
- 主队列是负责在主线程调度任务的
- 会随着程序启动一起创建
- 主队列只需要获取不用创建
dispatch_queue_t queue = dispatch_get_main_queue();
主队列,异步任务
- 不开线程,同步执行
- 主队列特点:如果主线程正在执行代码暂时不调度任务,等主线程执行结束后在执行任务
- 主队列又叫
全局串行队列
主队列,同步执行
- 程序执行不出来
(死锁)
死锁的原因
当程序执行到下面这段代码的时候
dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"%@ -- %d",[NSThread currentThread],i); });
主队列:如果主线程正在执行代码,就不调度任务
同步执行:如果第一个任务没有执行,就继续等待第一个任务执行完成,再执行下一个任务此时互相等待,程序无法往下执行(死锁)
全局并发队列
- 为了方便程序员开发提供的队列,与并发队列行为
相同
9. 同步异步串行并发的组合结果
没有开启新线程
,串行执行任务10. GCD延时函数使用
dispatch_after的定义
dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);
dispatch_after的参数
- 参数1 dispatch_time_t when
多少纳秒之后执行
- 参数2 dispatch_queue_t queue
任务添加到那个队列
- 参数3 dispatch_block_t block
要执行的任务
例子
//延时操作 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ });
11. GCD一次执行和单例实现
有的时候,在程序开发中,有些代码只想从程序启动就只执行一次,典型的应用场景就是“单例”
// MARK: 一次性执行- (void)once { static dispatch_once_t onceToken; NSLog(@"%ld", onceToken); dispatch_once(&onceToken, ^{ [NSThread sleepForTimeInterval:1.0]; NSLog(@"一次性吗?"); }); NSLog(@"come here");}
- dispatch_once是
线程安全
的,因为内部也有一把锁,苹果推荐使用该种方式来实现单例模式
测试
单例的2中实现
- 使用 dispatch_once 实现单例
+ (instancetype)sharedSingleton { static id instance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ instance = [[self alloc] init]; }); return instance;}
- 使用互斥锁实现单例
+ (instancetype)sharedSync { static id syncInstance; @synchronized(self) { if (syncInstance == nil) { syncInstance = [[self alloc] init]; } } return syncInstance;}
- 对比结果
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { long largeNumber = 1000 * 1000; // 测试互斥锁 CFAbsoluteTime start = CFAbsoluteTimeGetCurrent(); for (long i = 0; i < largeNumber; ++i) { [Singleton sharedSync]; } NSLog(@"互斥锁: %f", CFAbsoluteTimeGetCurrent() - start); // 测试 dispatch_once start = CFAbsoluteTimeGetCurrent(); for (long i = 0; i < largeNumber; ++i) { [Singleton sharedSingleton]; } NSLog(@"dispatch_once: %f", CFAbsoluteTimeGetCurrent() - start);}
12. GCD任务组
使用场景:在组里的异步任务都执行完毕后,再去执行其他操作,例如下载歌曲,等所有的歌曲都下载完毕之后 转到 主线程提示用户
- 方式一:dispatch_group_async常用用法
/** 调度组-在一组异步代码执行完毕后,统一获得通知 应用场景:将一组图像异步缓存到本地之后统一获得通知! */- (void)group1 { // 队列 dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 调度组 dispatch_group_t group = dispatch_group_create(); // 添加异步 dispatch_group_async(group, queue, ^{ [NSThread sleepForTimeInterval:1.0]; NSLog(@"download A %@", [NSThread currentThread]); }); dispatch_group_async(group, queue, ^{ [NSThread sleepForTimeInterval:0.5]; NSLog(@"download B %@", [NSThread currentThread]); }); dispatch_group_async(group, queue, ^{ NSLog(@"download C %@", [NSThread currentThread]); }); // 调度组通知 - 监听群组中,所有异步执行的代码完成后,得到通知 // 异步监听! dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"下载完成 %@", [NSThread currentThread]); }); NSLog(@"come here");}
- 方式二:添加enter & leave
// MARK: - 调度组 2- (void)group2 { // 1. 调度组 dispatch_group_t group = dispatch_group_create(); // 2. 队列 dispatch_queue_t q = dispatch_get_global_queue(0, 0); // dispatch_group_enter & dispatch_group_leave 必须成对出现 dispatch_group_enter(group); dispatch_group_async(group, q, ^{ NSLog(@"任务 1 %@", [NSThread currentThread]); // dispatch_group_leave 必须是 block 的最后一句 dispatch_group_leave(group); }); dispatch_group_enter(group); dispatch_group_async(group, q, ^{ NSLog(@"任务 2 %@", [NSThread currentThread]); // dispatch_group_leave 必须是 block 的最后一句 dispatch_group_leave(group); }); // 4. 阻塞式等待调度组中所有任务执行完毕 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); // 5. 判断异步 NSLog(@"OVER %@", [NSThread currentThread]);}
注意:
不要跟dispatch_barrier_async混淆,一个是所有任务都完成后,一个是某个任务中的一段代码需要交给另一个线程有序的串行执行
- GCD使用(多线程004)
- 使用GCD实现多线程
- 多线程GCD的使用
- GCD使用与多线程
- 多线程GCD的使用
- 多线程GCD的使用
- 多线程GCD的使用
- 多线程GCD的使用
- 多线程GCD的使用
- 多线程GCD的使用
- iOS多线程GCD使用
- 多线程GCD的使用
- ios多线程 GCD使用
- 使用GCD创建多线程
- 多线程GCD的使用
- iOS GCD多线程使用
- 多线程:GCD 基本使用
- GCD多线程的使用
- Shiro学习总结(10)——Spring集成Shiro
- RIP和OSPF到区别
- Aspose.Words生成报告
- 指针数组—成绩处理
- python内存管理
- GCD使用(多线程004)
- cocos2d-js中的回调函数中世界坐标系和节点坐标系的相互转换
- Manacher's Algorithm 马拉车算法
- Laravel5.* 打印出执行的sql语句
- 关于生成内部测试包二维码异地测试
- 去首尾空格
- MVC框架模式(一)
- tilda开机启动不透明
- 如何解决使用ARC后出现的PerformSelector may cause a leak because its selector is unknown