iOS多线程-NSThread
一、基本概念
1.计算机操作系统都有的基本概念,以下概念简单方式来描述。
1 进程: 一个具有一定独立功能的程序关于某个数据集合的一次运行活动。可以理解成一个运行中的应用程序。
2 线程: 程序执行流的最小单元,线程是进程中的一个实体。
3 队列: 装载线程任务的队形结构。
2.苹果官方定义
The term thread is used to refer to a separate path of execution for code.
The term process is used to refer to a running executable, which can encompass multiple threads.
线程用于指代一个独立执行的代码路径
进程用于指代一个可执行程序,他可以包含多个线程
Paste_Image.png
3.同步和异步主要影响:能不能开启新的线程
1 同步:只是在当前线程中执行任务,不具备开启新线程的能力
2 异步:可以在新的线程中执行任务,具备开启新线程的能力
4.并发和串行主要影响:任务的执行方式
1 并发:多个任务并发(同时)执行
2 串行:一个任务执行完毕后,再执行下一个任务
注意:
- 一个进程可有多个线程。
- 一个进程可有多个队列。
- 队列可分并发队列和串行队列。
二.NSThread API
1. 线程创建的两种方式
//直接创建并启动一个线程去Selector
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
//线程创建出来之后需要手动调用-start方法启动
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument NS_AVAILABLE(10_5, 2_0);
2. 线程操作之------启动,睡眠,取消,退出
Paste_Image.png1. 启动
使用init方式创建需要手动- (void)start;
例子-
//init初始化
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(thread:) object:@"thread"];
// 开启线程
[thread start];
例子二
//创建并自动开启方法
[NSThread detachNewThreadSelector:@selector(thread1:) toTarget:self withObject:@"thread1"];
2. 线程睡眠的两种方法
//根据NSDate传入睡眠时间
+ (void)sleepUntilDate:(NSDate *)date;
//直接传入NSTimeInterval
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
NSThread的sleepUntilDate与runloop的runUntilDate:
深入理解RunLoop
![1.jpg](https://img.haomeiwen.com/i326255/a503a03b9938f3ad.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
sleepUntilDate:相当于执行一个sleep的任务。在执行过程中,即使有其他任务传入runloop,runloop也不会立即响应,必须sleep任务完成之后,才会响应其他任务
runUntilDate:虽然会阻塞线程,阻塞过程中并不妨碍新任务的执行。当有新任务的时候,会先执行接收到的新任务,新任务执行完之后,如果时间到了,再继续执行runUntilDate:之后的代码
例子:
//让线程睡眠2秒(阻塞2秒)
[NSThread sleepForTimeInterval:2];
//直接传入NSTimeInterval
[NSThread sleepUntilDate:[NSDate datewithTimeIntervalSinceNow:2]];
3.取消
对于线程的取消,NSThread提供了一个取消的方法和一个属性,调用-cancel方法并不会立刻取消线程,它仅仅是将cancelled属性设置为YES。cancelled也仅仅是一个用于记录状态的属性。线程取消的功能需要我们在main函数中自己实现
- (void)cancel NS_AVAILABLE(10_5, 2_0);
要实现取消的功能,我们需要自己在线程的main函数中定期检查isCancelled状态来判断线程是否需要退出
,当isCancelled为YES的时候,我们手动退出。如果我们没有在main函数中检查isCancelled状态,那么调用-cancel将没有任何意义
5.退出
与充满不确定性的-cancel相比,-exit函数可以让线程立即退出。
+ (void)exit;
-exit属于核弹级别终极API,调用之后会立即终止线程,即使任务还没有执行完成也会中断。这就非常有可能导致内存泄露等严重问题,所以一般不推荐使用。
三. 线程之间的通讯
1. 从主线程把耗时的任务丢给辅助线程,当任务完成之后辅助线程再把结果传回主线程传,这些线程通讯一般用的都是perform方法
//将selector丢给主线程执行,可以指定runloop mode
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array;
//将selector丢给主线程执行,runloop mode默认为common mode
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
//将selector丢个指定线程执行,可以指定runloop mode
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait modes:(nullable NSArray *)array NS_AVAILABLE(10_5, 2_0);
//将selector丢个指定线程执行,runloop mode默认为default mode
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
2. 主线程相关
// 获得主线程
+ (NSThread *)mainThread;
// 是否为主线程
- (BOOL)isMainThread;
// 是否为主线程
+ (BOOL)isMainThread;
// 获得当前线程
NSThread *current = [NSThread currentThread];
3. 线程优先级
3.1 NSQualityOfService主要有5个枚举值,优先级别从高到低排布:
1. NSQualityOfServiceUserInteractive:最高优先级,主要用于提供交互UI的操作,比如处理点击事件,绘制图像到屏幕上
2. NSQualityOfServiceUserInitiated:次高优先级,主要用于执行需要立即返回的任务
3. NSQualityOfServiceDefault:默认优先级,当没有设置优先级的时候,线程默认优先级
4. NSQualityOfServiceUtility:普通优先级,主要用于不需要立即返回的任务
5. NSQualityOfServiceBackground:后台优先级,用于完全不紧急的任务
一般主线程和没有设置优先级的线程都是默认优先级。
3.2 线程的调度优先级 priority(优先级)
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
- (double)threadPriority;
- (BOOL)setThreadPriority:(double)p;
调度优先级的取值范围是0.0 - 1.0,默认0.5,值越大,优先级越高
4. 线程通知
通知相关的三种形式
//由当前线程派生出第一个其他线程时发送,一般一个线程只发送一次
NSString * const NSWillBecomeMultiThreadedNotification;
//这个通知目前没有实际意义,可以忽略
NSString * const NSDidBecomeSingleThreadedNotification;
//线程退出之前发送这个通知
NSString * const NSThreadWillExitNotification;
例子
- (void)viewdidload{
// 创建线程
self.thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain) object:nil];
//设置线程优先级
self.thread.qualityOfService = NSQualityOfServiceDefault;
//启动线程
[self.thread start];
}
- (void)threadMain {
//给线程设置名字. 可以不设置
[[NSThread currentThread] setName:@"myThread"];
// 给线程添加runloop
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
//给runloop添加数据源
[runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode];
//④:检查isCancelled
while (![[NSThread currentThread] isCancelled]) {
//⑤启动runloop
[runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:10]];
}
}
参考: iOS多线程篇
二、iOS多线程对比
- NSThread
每个NSThread对象对应一个线程,真正最原始的线程。
1)优点:NSThread 轻量级最低,相对简单。
2)缺点:手动管理所有的线程活动,如生命周期、线程同步、睡眠等。
- NSOperation
自带线程管理的抽象类。
1)优点:自带线程周期管理,操作上可更注重自己逻辑。
2)缺点:面向对象的抽象类,只能实现它或者使用它定义好的两个子类:NSInvocationOperation 和 NSBlockOperation。
- GCD
Grand Central Dispatch (GCD)是Apple开发的一个多核编程的解决方法。
1)优点:最高效,避开并发陷阱。
2)缺点:基于C实现。
- 选择小结
1)简单而安全的选择NSOperation实现多线程即可。
2)处理大量并发数据,又追求性能效率的选择GCD。
3)NSThread本人选择基本上是在做些小测试上使用,当然也可以基于此造个轮子。
更多精彩内容请关注“IT实战联盟”哦~~~
IT实战联盟.jpg