关于多线程
(一).多线程
主线程:显示/刷新UI界面,处理UI事件
子线程:处理耗时操作 例如网络访问
[图片上传失败...(image-d0f9b0-1526873962369)]
1.NSThread创建方式
(1).alloc/init-start
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(longOperationa :) object:@"THREADkn"];
[thread start]; //会开启子线程执行longOperationa : 方法
(2).detachNewThreadSelector
[NSThread detachNewThreadSelector:@selector(longOperation:) toTarget:self withObject:@"DETACH"];
detachNewThreadSelector 类方法不需要启动,会自动创建子线程并执行 @selector 方法。
(3).
[self performSelectorInBackground:@selector(longOperation:) withObject:@"PERFORM"];
performSelectorInBackground 是 NSObject 的分类方法。
会自动在后台线程执行 @selector 方法。
没有 thread 字眼,隐式创建并启动线程。
所有 NSObject 都可以使用此方法,在其他线程执行方法
2.线程状态
(1).创建
(2).就绪 向线程对象发送start消息,线程对象被加入可调度线程池等待CPU调度 其他两个方法就会直接实例化一个线程对象加入可调度线程池
(3).运行 CPU负责调度可调度池中线程的执行 线程执行完毕前 状态就会在就绪和运行之间来回切换 程序员无法干预
(4).阻塞 休眠或阻塞线程执行 sleepForTimeInterval:休眠到指定时长 sleepUntilDate:休眠到指定日期 @synchronized 互斥锁
(5).死亡 正常死亡 就是线程执行完毕 如果是非正常死亡 exit/return 在终止线程之前,应该注意释放之前分配的对象!
3.线程属性
(1).线程的名字name -作用就是在大型的商业项目中,通常需要在程序崩溃的时候,获取程序准确执行的所在线程
(2).线程优先级-threadPriority 0~1.0线程优先级 默认是0.5 优先级高只是保证CPU调度的可能性会高 不是一定 开发中不要修改,调度频率快慢由CPU决定
(3).栈区大小-stackSize 默认情况下,无论主线程还子线程,栈区的大小都512K 可以修改
4.资源共享
多个线程同时调用同一个线程方法,共享同一个资源,就要用互斥锁 @synchronized(self) {}
保证同一时间内只有一条线程能执行,互斥锁效率就越差。
原子属性:多个线程在写入原子属性时调用setter方法保证只有一个线程在写入内部实现是通过自旋锁效率比互斥锁高。
互斥锁:如果发现有其他线程正在执行锁定的代码,线程会进入休眠状态,等待其他线程执行完毕,打开锁之后,线程会被唤醒。
自旋锁:如果发现有其他线程正在执行锁定的代码,线程会以死循环的方式,一直等待锁定代码执行完成。
5.线程间通讯
(1).在后台子线程线程下载图像
[self performSelectorInBackground:@selector(downloadImage) withObject:nil];
(2).主线程更新UI
[self performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO]
6.GCD
(1).同步/异步
决定是否等待当前执行才执行下一步 同步不会开启线程 异步开启线程
(2)Block
全局Block:block中没有任何外部变量
栈区Block:block中使用了外部变量
mrc情况下外部变量在栈区中由程序员管理.
arc情况下会复制到堆中, 如果想修改block中变量 必须外部用__block修饰变量
block 用copy修饰
(3)队列
串行队列:先进先出,按顺序执行,无论异步还是同步需要执行完一个任务才能执行下一个任务
全局队列/并发队列:先进先出, 如果是同步执行,就是按顺序执行 如果是异步执行 就会同时执行多个任务.
主队列:不会开启线程 都是在主线程中执行 主线程空闲的时候会调用 同步执行就会死锁
同步任务作用:任务依赖/Barrier 例如登陆时同步任务 等登陆完毕后才能执行后面任务
(4)单例
+ (instancetype)sharedSingleton {
static id instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
(5).延时
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), task);
(6)调度组
原理:就是一组任务都做调度组中. 监听一组任务完成,才通知其他操作
应用场景:缓存网络图片
实现步骤:
1).创建调度组 2).创建全局队列 3).将任务添加到队列和调度组中 4).监听任务完成
7.NSOperation
原理:基于GCD,默认异步执行,就是将操作(NSBlockOperation)添加队列中.
比GCD多的功能:
(1).设置最大并发数
最大并发数:self.queue.maxConcurrentOperationCount = 2
队列中的操作数 self.queue.operationCount
(2).暂停
暂停 self.queue.isSuspended挂起
(3).取消
取消所有操作 self.queue cancelAllOperations
多线程的好处跟坏处
1、好处:
1、使用线程可以把程序中占据时间长的任务放到后台去处理,如图片、视频的下载
2、发挥多核处理器的优势,并发执行让系统运行的更快、更流畅,用户体验更好
缺点:
1、大量的线程降低代码的可读性,
2、更多的线程需要更多的内存空间
3、当多个线程对同一个资源出现争夺的时候要注意线程安全的问题。
iOS有三种多线程编程的技术:
1、NSThread(两种创建方式)
[NSThread detachNewThreadSelector:@selector(doSomething:) toTarget:self withObject:nil];
NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(doSomething:) object:nil];
[myThread start];
2、NSOperationQueue
NSOperationQueue*oprationQueue= [[NSOperationQueuealloc] init];
oprationQueueaddOperationWithBlock:^{
//这个block语句块在子线程中执行
}
3、Grand Central Dispatch (GCD)
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//耗时的操作
dispatch_async(dispatch_get_main_queue(), ^{
//更新界面
});
});
PS:不显示的创建线程的方法:
用NSObject的类方法performSelectorInBackground:withObject:创建一个线程:[Obj performSelectorInBackground:@selector(doSomething) withObject:nil];