iOS笔记_12_多线程
来源:互联网 发布:漫画打印排版软件 编辑:程序博客网 时间:2024/06/05 05:01
主线程
- 一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”(刷新UI界面最好在主线程中做,在子线程中可能会出现莫名其妙的BUG)
- 主线程的作用
- 显示\刷新UI界面
- 处理UI事件(比如点击事件、滚动事件、拖拽事件等)
- 注意点
- 别将比较耗时的操作放到主线程中
- 耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验
- iOS中多线程的实现方案
- pthread(c语言,程序员管理)
- 一套通用的多线程API
- 适用于Unix\Linux\Windows等系统
- 跨平台\可移植
- 使用难度大
- NSThread(oc语言,程序员管理)
- 使用更加面向对象
- 简单易用,可直接操作线程对象
- GCD(c语言,自动管理)
- 旨在替代NSThread等线程技术
- 充分利用设备的多核
- NSOperation(oc语言,自动管理)
- 基于GCD(底层是GCD)
- 比GCD多了一些更简单实用的功能
- 使用更加面向对象
- pthread(c语言,程序员管理)
NSThread
- 一个NSThread对象就代表一条线程
- 创建、启动线程
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];[thread start];// 线程一启动,就会在线程thread中执行self的run方法
- 常见相关用法
+ (NSThread *)mainThread; // 获得主线程- (BOOL)isMainThread; // 是否为主线程+ (BOOL)isMainThread; // 是否为主线程// 获取当前线程NSThread *current = [NSThread currentThread];// 线程的名字- (void)setName:(NSString *)n;- (NSString *)name;
- 其他创建线程方式
- 优点:简单快捷
- 缺点:无法对线程进行更详细的设置
// 创建线程后自动启动线程[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];// 隐式创建并启动线程[self performSelectorInBackground:@selector(run) withObject:nil];
- 控制线程的状态
// 启动线程- (void)start; // 进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态// 阻塞(暂停)线程+ (void)sleepUntilDate:(NSDate *)date;+ (void)sleepForTimeInterval:(NSTimeInterval)ti;// 进入阻塞状态// 强制停止线程+ (void)exit;// 进入死亡状态// 注意:一旦线程停止(死亡)了,就不能再次开启任务
互斥锁
- 互斥锁使用格式
@synchronized(锁对象) { // 需要锁定的代码 }// 注意:锁定1份代码只用1把锁,用多把锁是无效的
原子和非原子属性
- OC在定义属性时有nonatomic和atomic两种选择
- atomic:原子属性,为setter方法加锁(默认就是atomic)
- nonatomic:非原子属性,不会为setter方法加锁
- nonatomic和atomic对比
- atomic:线程安全,需要消耗大量的资源
- nonatomic:非线程安全,适合内存小的移动设备
- iOS开发的建议
- 所有属性都声明为nonatomic
- 尽量避免多线程抢夺同一块资源
- 尽量将加锁、资源抢夺的业务逻辑交给服务器端处理,减小移动客户端的压力
线程间的通信
- 什么叫做线程间通信
- 在1个进程中,线程往往不是孤立存在的,多个线程之间需要经常进行通信
- 线程间通信的体现
- 1个线程传递数据给另1个线程
- 在1个线程中执行完特定任务后,转到另1个线程继续执行任务
- 线程间通信常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
GCD
- 本质及优点
- 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器”
- GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
- 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
- 使用GCD的步骤
- 定制任务
- 确定想做的事情
- 将任务添加到队列中
- GCD会自动将队列中的任务取出,放到对应的线程中执行
- 任务的取出遵循队列的FIFO原则:先进先出,后进后出
- 定制任务
- 执行任务常用的函数
// 用同步的方式执行任务dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);// queue:队列// block:任务// 注意:使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列。同步方式会阻塞当前队列。// 用异步的方式执行任务dispatch_async(dispatch_queue_t queue, dispatch_block_t block);dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block);// 在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行// 注意:这个queue不能是全局的并发队列
- 同步和异步的区别
- 同步:只能在当前线程中执行任务,不具备开启新线程的能力
- 异步:可以在新的线程中执行任务,具备开启新线程的能力
- 队列的类型
- 并发队列(Concurrent Dispatch Queue)
- 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
- 并发功能只有在异步(dispatch_async)函数下才有效
- 串行队列(Serial Dispatch Queue)
- 让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)
- 并发队列(Concurrent Dispatch Queue)
- 并发队列
// 创建并发队列dispatch_queue_t queue = dispatch_queue_create("队列名称", DISPATCH_QUEUE_CONCURRENT)// 第一个参数 const char *label 队列名称 // 第二个参数 dispatch_queue_attr_t attr 队列的类型 DISPATCH_QUEUE_CONCURRENT 表示并发队列// 获得全局的并发队列dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); // 第一个参数 dispatch_queue_priority_t priority 队列的优先级// 第二个参数 unsigned long flags 此参数暂时无用,用0即可#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台
- 串行队列
// 创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)dispatch_queue_t queue = dispatch_queue_create("队列名称", NULL); // 获得主队列,主队列也是串行队列dispatch_queue_t queue = dispatch_get_main_queue();
- 线程间的通信
// 从子线程回到主线程dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 执行耗时的异步操作... dispatch_async(dispatch_get_main_queue(), ^{ // 回到主线程,执行UI刷新操作 });});
- 延迟操作
- iOS中延迟操作有3种
- 调用NSObject的方法
- 使用GCD函数
- 使用NSTimer
- iOS中延迟操作有3种
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];// 2秒后再调用self的run方法dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 2秒后执行这里的代码...});[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(test) userInfo:nil repeats:NO];
- 一次性代码
// 使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{ // 只执行1次的代码(这里面默认是线程安全的)});
- 快速迭代
// 使用dispatch_apply函数能进行快速迭代遍历dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index){ // 执行10次代码,index顺序不确定});
- 队列组
- 可以满足的一种需求(当然也可以用依赖线程依赖实现,下文会提到)
- 首先:分别异步执行2个耗时的操作
- 其次:等2个异步操作都执行完毕后,再回到主线程执行操作
- 可以满足的一种需求(当然也可以用依赖线程依赖实现,下文会提到)
dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 执行1个耗时的异步操作});dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 执行1个耗时的异步操作});dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 等前面的异步操作都执行完毕后,回到主线程...});
单例模式
- 单例模式的作用
- 可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问
- 从而方便地控制了实例个数,并节约系统资源
- 单例模式的使用场合
- 在整个应用程序中,共享一份资源(这份资源只需要创建初始化1次)
- 实现步骤:
// 在.m中保留一个全局的static的实例static id _instance;// 重写allocWithZone:方法,在这里创建唯一的实例(注意线程安全),alloc方法会调用allocWithZone:方法+ (instancetype)allocWithZone:(struct _NSZone *)zone{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [super allocWithZone:zone]; }); return _instance;}// 提供1个类方法让外界访问唯一的实例+ (instancetype)sharedInstance{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _instance = [[self alloc] init]; }); return _instance;}// 实现copyWithZone:方法- (id)copyWithZone:(struct _NSZone *)zone{ return _instance;}
NSOperation
- NSOperation和NSOperationQueue实现多线程的具体步骤
- 先将需要执行的操作封装到一个NSOperation对象中
- 然后将NSOperation对象添加到NSOperationQueue中
- 系统会自动将NSOperationQueue中的NSOperation取出来
- 将取出的NSOperation封装的操作放到一条新线程中执行
- NSOperation是抽象类,使用NSOperation子类的方式有3种
- NSInvocationOperation
- NSBlockOperation
- 自定义子类继承NSOperation,实现内部相应的方法
- NSInvocationOperation
// 创建NSInvocationOperation对象- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg;// 调用start方法开始执行操作- (void)start;// 一旦执行操作,就会调用target的sel方法// 注意:默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作。只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
- NSBlockOperation
// 创建NSBlockOperation对象+ (id)blockOperationWithBlock:(void (^)(void))block;// 通过addExecutionBlock:方法添加更多的操作- (void)addExecutionBlock:(void (^)(void))block;// 注意:只要NSBlockOperation封装的操作数 > 1,就会异步执行操作
- NSOperationQueue
// 添加操作到NSOperationQueue中- (void)addOperation:(NSOperation *)op;- (void)addOperationWithBlock:(void (^)(void))block;
- 相关操作
// 设置最大并发数- (NSInteger)maxConcurrentOperationCount;- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;// 取消队列的所有操作- (void)cancelAllOperations;// 提示:也可以调用NSOperation的- (void)cancel方法取消单个操作// 暂停和恢复队列- (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列- (BOOL)isSuspended;
- 操作依赖
- NSOperation之间可以设置依赖来保证执行顺序
- 可以在不同queue的NSOperation之间创建依赖关系
[operationB addDependency:operationA]; // 操作B依赖于操作A
- 操作的监听
// 可以监听一个操作的执行完毕- (void (^)(void))completionBlock;- (void)setCompletionBlock:(void (^)(void))block;
- 自定义NSOperation
- 只需要重写- (void)main方法,在里面实现想执行的任务
- 注意点
- 自己创建自动释放池(因为如果是异步操作,无法访问主线程的自动释放池)
- 经常通过- (BOOL)isCancelled方法检测操作是否被取消,对取消做出响应
计算时间差
NSDate *start = [NSDate date];// 上下两句代码间放需要计算时间的程序NSDate *end = [NSDate date];NSLog(@"%f", [end timeIntervalSinceDate:start]);
CFTimeInterval begin = CFAbsoluteTimeGetCurrent();// 上下两句代码间放需要计算时间的程序CFTimeInterval end = CFAbsoluteTimeGetCurrent();NSLog(@"%f", end - begin);
小结:
1、在GCD中,方法如果用异步函数可以开启子线程做事情,该方法中的程序会顺序执行到底,然后再返回去开启子线程执行内部的操作。如果是同步函数,则不能开启子线程,里面的同步函数只能一个一个执行下去。
2、如果在主队列中调用同步函数,容易造成死锁。
0 0
- iOS笔记_12_多线程
- ios--多线程复习笔记
- iOS 多线程笔记
- iOS多线程笔记
- iOS多线程学习笔记
- Ios多线程学习笔记
- iOS 学习笔记 多线程
- iOS多线程学习笔记
- iOS多线程开发笔记
- iOS多线程笔记
- Linux 学习笔记_12_文件共享服务_2_FTP应用--vsftpd
- Linux 学习笔记_12_文件共享服务_4_SSH
- C++ Primer 学习笔记_12_指针(续)
- 多线程(IOS)学习笔记
- iOS 多线程笔记_( 1 )
- iOS 多线程笔记_(2)
- iOS笔记—多线程基础
- iOS学习笔记--02 多线程
- STM32_GPIO
- 【转】asp.net Forms表单验证 使用经验及验证流程分析
- CSS样式三--盒子模型
- 137. Single Number II
- 剑指Offer--024-二叉搜索树的后序遍历序列
- iOS笔记_12_多线程
- 快速排序的实现
- 写一个类似大众点评的城市选择控件
- LeetCode *** 82. Remove Duplicates from Sorted List II
- AJAX各种实现方式比较
- Java基础-初始化
- 实现TableView的上拉加载和下拉刷新
- HDU 1556 Color the ball
- 破坏单例模式