iOS线程的同步异步问题
来源:互联网 发布:手机淘宝店铺红包链接 编辑:程序博客网 时间:2024/04/30 05:26
1. 进程、线程、多线程
进程 : 可以简单的理解, 一个应用程序就是一个进程;
线程 : 可以理解为在app中往后运行的通道, 一个进程可以有多个线程;
多线程: 并不是所有的框架都支持多线程, 必须要有多核的cpu支持才行, 单核cpu即使开了多线程运行速度也不会有变化, 开的线程数有几种说法, 其一: 线程数为手机核数的2到3倍, 比如一个双核手机, 开线程数为4到6条; 其二: 根据网络状态, 如果不是wifi状态, 一般开3到4条, 如果是wifi状态,可以开启5到6条;
多线程的目的: 将耗时的操作放在后台, 而不影响主线程与用户的交互;
多线程示意图:
多线程优缺点:
优点:
1). 能适当提高程序的执行效率,
2). 能适当提高资源的利用率
3). 线程上的任务执行完成后, 线程会自动销毁
缺点:
1). 开启线程需要占用一定的内存空间 ( 默认情况下,每一个线程都占用512Kb ) ( 以前主线程占用1MB空间,现在主线程和子线程都只占用512KB )
2). 如果开启大量的线程,会占用大量的内存空间,CPU会在N个线程之间切换,消耗大量的CPU资源,每个线程被调度的次数会降低,线程的执行效率降低;
3). 程序设计更加复杂,比如线程间的通信、多线程的数据共享
2 . 串行并行,同步异步
串行简单理解就是一个人负责多件事情, 并行是多个人一起负责多个事件;
同步是多个事件按顺序往下执行, 异步是多个事件在多个通道同时执行;
3 . 异步操作和多线程的联系和区别
异步大多数是多线程, 但也不一定是, block方法回调和dispatch的定时函数也是异步操作, 但不一定是多线程, 视当前的代码环境而定;
4. 多编程的技术方案
- 一套通用的多线程API
- 适用于 Unix / Linux / Windows 等系统
- 跨平台\可移植
- 使用难度大
- 使用更加面向对象
- 简单易用,可直接操作线程对象
- 旨在替代NSThread等线程技术
- 充分利用设备的多核
- 基于GCD(底层是GCD)
- 比GCD多了一些更简单实用的功能
- 使用更加面向对象
代码演练:
一、pthread
- 知道
C
语言中void *
与OC
中的id
类似 - 使用
[NSThread currentThread]
能够在任何多线程技术中,查询当前代码执行所在线程number == 1
,表示主线程number != 1
,表示其他线程
- pthread 是 POSIX 多线程开发框架,由于是跨平台的 C 语言框架,在苹果的头文件中并没有详细的注释
- 要查阅 pthread 有关资料,可以访问 http://baike.baidu.com
- 创建工程
pthread
- 导入头文件
#import <pthread.h>
touchesBegan
创建线程- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { /* 参数: 1. pthread_t *restrict: 需要 pthread_t(线程标示符) 类型的指针, C语言中的 _t/Ref,都是结构体 2. const pthread_attr_t *restrict: 线程属性 3. void *(*)(void *): 线程调用的函数 void * (*) (void *) 返回值 函数地址 参数类型 4. void *restrict: 第三个参数的参数 返回值: 0: 表示成功 非0表示失败,一个数字对应一个失败原因 */ // 1> 定义一个 C 字符串 // char *cName = "zhangsan"; // 2> OC 的字符串 NSString *name = @"lisi"; pthread_t threadId = NULL; int result = pthread_create(&threadId, NULL, demo, (__bridge void *)(name)); if (result == 0) { NSLog(@"成功"); } else { NSLog(@"失败"); } }
pthread
线程调用函数/// 线程调用函数void *demo(void * param) { NSLog(@"线程 = %@", [NSThread currentThread]); // 1.打印C语言字符串// NSLog(@"param = %s", param); // 2.打印OC字符串// NSString *name = (__bridge NSString *)(param); NSLog(@"param = %@", name); return NULL;}
- 在 C 语言中,没有对象的概念,对象是以结构体的方式来实现的
- 通常,在 C 语言框架中,对象类型以 _t/Ref 结尾,而且声明时不需要使用
*
C
语言中的void *
和OC
中的id
是类似的- 内存管理
- 在
OC
中,如果是ARC
开发,编译器会在编译时,根据代码结构,自动添加 retain/release/autorelease - 但是,
ARC
只负责管理OC
部分的内存管理,而不负责C
语言 代码的内存管理 - 开发过程中,如果使用的
C
语言框架出现 retain/create/copy/new 等字样的函数,大多都需要 release,否则会出现内存泄漏
- 在
- 在混合开发时,如果在
C
和OC
之间传递数据,需要使用__bridge
进行桥接,桥接的目的就是为了告诉编译器如何管理内存- 桥接的添加可以借助
Xcode
的辅助功能添加 - MRC 中不需要使用桥接
- 桥接的添加可以借助
二、NSThread
NSThread的3中创建线程方式
- NSThread 创建线程的三种方式
- NSThread 类的 alloc/init 创建线程
- NSThread 的 类方法 创建线程
- NSObject 分类方法 创建线程
- 创建工程
NSThread创建线程
创建线程第一种方式:
使用
NSThread
类的alloc/init
创建线程#pragma mark - 创建线程 /// 使用 alloc / init 创建线程 - (void)thread1 { NSLog(@"thread1 begin %@", [NSThread currentThread]); // 创建 NSThread 对象 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(longOperation:) object:@"hello"]; // 启动线程 [thread start]; NSLog(@"thread1 end %@", [NSThread currentThread]); }
定义 子线程 执行的方法
#pragma mark - 线程执行方法 - (void)longOperation:(id)param { NSLog(@"longOperation begin %@", [NSThread currentThread]); NSLog(@"longOperation end %@", [NSThread currentThread]); }
- 在
touchesBegan:withEvent:
调用- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { [self thread1]; }
- 小结:
- 在
OC
中,任何一个线程执行代码都是从上向下顺序执行的 [thread start]
; 执行后,会在另外一个线程执行longOperation:
方法
- 在
- 使用
NSThread
的类方法
detachNewThreadSelector
创建线程/// 使用 NSThread 类方法 - (void)thread2 { [NSThread detachNewThreadSelector:@selector(longOperation:) toTarget:self withObject:@"hello2"]; }
detachNewThreadSelector
类方法不需要启动
,会自动创建线程并执行@selector
方法
- 使用
NSObject
分类方法
performSelectorInBackground
创建线程/// 使用 NSObject 分类方法 - (void)thread3 { [self performSelectorInBackground:@selector(longOperation:) withObject:@"hello3"]; }
- 小结:
performSelectorInBackground
是NSObject
的分类方法- 会自动在后台线程执行 @selector 方法
- 没有 thread 字眼,隐式创建并启动线程
- 所有 NSObject 都可以使用此方法,在其他线程执行方法
- Swift 中不支持
- 小结:
示意图:
- 状态说明
- 新建
- 实例化线程对象
- 就绪
- 向线程对象发送 start 消息,线程对象被加入 可调度线程池 等待 CPU 调度
- detach 方法和 performSelectorInBackground 方法会直接实例化一个线程对象并加入 可调度线程池
- 运行
- CPU 负责调度可调度线程池中线程的执行
- 线程执行完成之前,状态可能会在就绪和运行之间来回切换
- 就绪和运行之间的状态变化由 CPU 负责,程序员不能干预
- 阻塞
- 当满足某个预定条件时,可以使用休眠或锁阻塞线程执行
- sleepForTimeInterval:休眠指定时长
- sleepUntilDate:休眠到指定日期
- @synchronized(self):互斥锁
- 死亡
- 正常死亡
- 线程执行完毕
- 非正常死亡
- 当满足某个条件后,在线程内部中止执行
- 当满足某个条件后,在主线程中止线程对象
- 注意:一旦线程停止(死亡)了,就不能再次开启任务
- 正常死亡
- 新建
控制线程状态的方法
- 启动
- [thread start]
- 线程进入就绪状态,线程对象被加入 可调度线程池 等待 CPU 调度, 当线程执行完毕后自动进入死亡状态
- [thread start]
- 休眠
- 方法执行过程中,符合某一条件时,可以利用 sleep 方法让线程进入 阻塞 状态
- sleepForTimeInterval 从现在起睡多少秒
- sleepUntilDate 从现在起睡到指定的日期
- 方法执行过程中,符合某一条件时,可以利用 sleep 方法让线程进入 阻塞 状态
- 死亡
- [NSThread exit]
- 一旦强行终止线程,后续的所有代码都不会被执行
- 注意:在终止线程之前,应该注意释放之前分配的对象!
- [NSThread exit]
- 取消
- [_thread cancel]
- 并不会直接取消线程
- 只是给线程对象添加 isCancelled 标记
- 需要在线程内部的关键代码位置,增加判断,决定是否取消当前线程
- [_thread cancel]
GCD的方法很多,用法也很多,这里只列举一些常用的方法。常用的方法包括:
- 同步、非阻塞执行
- 异步非阻塞执行
- 一次性执行
- 延迟执行
- 线程队列串行执行
- 线程队列控制(屏障,同步等待,线程暂停和恢复,线程信号量控制等)
四、NSOperation
NSOperation需要在NSOperationQueue中使用,通过queue可以实现先进先出的队列任务,可以添加或取消任务,NSOperation有2个重要的子类,分别是:NSInvocationOperation,NSBlockOperation,分别表示调用一个方法或调用一个block的任务。 NSOperation是比GCD更高层次的api,相同的线程操作如果能用NSOperation操作就尽量用,不能实现的线程操作才使用GCD.相比GCD,NSOperation还有个好处,就是任务可以被取消,而GCD不可以。
有2个值得注意的地方,第一个是mainQueue,第二个是maxConcurrentOperationCount。
mainQueue是通过 NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
获取到,它代表主队列,也就是UI队列,所以用到mainQueue队列的时候一般用于更新ui界面,且特别注意在这个队列中执行的方法,要考虑到会不会阻塞进程。
maxConcurrentOperationCount:最多有多少个队列可以同时执行,默认是5,当设置为1是,队列是一个串行队列,设置>1时,队列是一个并行队列。但是在主队列上设置同时执行的任务是没有效果的!如果没有设置最大并发数,那么并发的个数是由系统内存和CPU决定的,可能内存多久开多一点,内存少就开少一点。
- 取消队列的所有操作
[queue cancelAllOperations];
- 暂停队列恢复
操作依赖
- iOS线程的同步异步问题
- iOS线程的同步和异步
- ios的线程和同步异步操作
- [ios] dispatch_get_main_queue 的同步异步问题
- iOS多线程相关,同步异步的问题
- iOS实现多个异步线程同步的操作
- 线程的同步与异步
- 线程的同步和异步
- 线程的同步和异步
- 线程的同步与异步
- 线程的同步和异步
- 线程的同步和异步
- 线程的同步和异步
- 线程的同步与异步
- 线程的同步和异步
- 线程的同步和异步
- 线程的同步与异步
- 线程的同步与异步
- 一篇深刻的文章
- eclipse building workspace 卡住
- 纯css实现Tab切换的两种方法
- ubuntu下的/var/cache/apt/archives文件夹;用apt-get命令下载的deb软件包
- MySQL存储过程
- iOS线程的同步异步问题
- 冬天
- 数据库Oracle与Mysql语法对比:变量赋值
- zTree插件和layer弹出层结合进行添加,删除,修改操作时,针对出现调用父父页面方法出现问题,解决方法
- mysql 免安装版本安装服务器
- CSS3:制作3D旋转导航综合练习题
- 在一个webview里跳转到新的activity里,webview无法响应js事件的问题
- Android Studio CMakeLists.txt文件配置
- RN1.学习记录开篇