NSThread
1.NSThread共有3种创建方式
1.init
/// init 创建完成之后需要手动start
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(log) object:nil];
[thread start];
2.detachNewThreadSelector
/// 创建完成自动启动
[NSThread detachNewThreadSelector:@selector(log) toTarget:self withObject:nil];
3.performSelectorInBackground
/// 创建完成自动启动
[self performSelectorInBackground:@selector(log) withObject:nil];
2.NSThread类方法
/// 获取当前线程 number = 1 为主线程 否则为子线程
[NSThread currentThread];
/// 线程休眠 单位秒
[NSThread sleepForTimeInterval:2];
/// 线程休眠到指定时间
[NSThread sleepUntilDate: [NSDate date]];
/// 获取主线程
[NSThread mainThread];
/// 退出线程
[NSThread exit];
/// 判断当前线程是否为主线程
[NSThread isMainThread];
/// 判断当前线程是否为多线程
[NSThread isMultiThreaded];
3.NSThread的属性
/// 线程是否在执行
@property (readonly, getter=isExecuting) BOOL executing;
/// 线程是否被取消
@property (readonly, getter=isFinished) BOOL finished;
/// 线程是否完成
@property (readonly, getter=isCancelled) BOOL cancelled;
/// 是否是主线程
@property (readonly) BOOL isMainThread;
GCD
GCD会自动利用更多的CPU内核,自动管理线程的生命周期(创建、调度、销毁)
1.GCD的基本概念
- 任务(block):任务就是需要执行的代码块。
- 队列:装载线程任务的序列。
- 并发队列:同一时间,队列中的线程可以一起执行,实质是CPU在多条线程之前快速的切换。
- 串行队列:线程按照先进先出的顺序执行。
- 同步:不开启新线程,一个接一个顺序执行。
- 异步:开启多个线程,任务同一时间可以一起执行。
1.队列的创建
/// 传入两个参数,第一个参数为队列的标识符,可为空,第二个参数用来表示创建串行队列DISPATCH_QUEUE_SERIAL或者并发队列DISPATCH_QUEUE_CONCURRENT
/// 串行队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
/// 并发队列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
/// 主队列
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
});
});
/// 全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 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 // 后台优先级
2.同步异步的创建
同步:用dispatch_sync
异步:用dispatch_async
/// 同步执行
dispatch_async(dispatch_get_global_queue(0, 0), ^{
/// 代码块
});
/// 异步执行
dispatch_sync(dispatch_get_global_queue(0, 0), ^{
/// 代码块
});
3.GCD的使用
由于CGD存在多种队列和不同的执行方式,所以它们的组合方式有许多种。
1.串行同步
2.串行异步
3.并发同步
4.并发异步
5.主队列同步
6.主队列异步
- 串行同步
/// 创建串行队列
dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
/// 创建同步线程
dispatch_sync(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"串行同步1 %@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"串行同步2 %@",[NSThread currentThread]);
}
});
输出结果都在主线程,顺序执行
串行同步1 {number = 1, name = main}
串行同步1 {number = 1, name = main}
串行同步2 {number = 1, name = main}
串行同步2 {number = 1, name = main}
- 串行异步
/// 创建串行队列
dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_SERIAL);
/// 创建异步线程
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"串行异步1 %@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"串行异步2 %@",[NSThread currentThread]);
}
});
输出结果在子线程,顺序执行
串行异步1 {number = 5, name = (null)}
串行异步1 {number = 5, name = (null)}
串行异步2 {number = 5, name = (null)}
串行异步2 {number = 5, name = (null)}
- 并发同步
/// 创建并发队列
dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_CONCURRENT);
/// 创建同步线程
dispatch_sync(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"并发同步1 %@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"并发同步2 %@",[NSThread currentThread]);
}
});
输出结果都在主线程,顺序执行
并发同步1 {number = 1, name = main}
并发同步1 {number = 1, name = main}
并发同步2 {number = 1, name = main}
并发同步2 {number = 1, name = main}
- 并发异步
/// 创建并发队列
dispatch_queue_t queue = dispatch_queue_create("serial", DISPATCH_QUEUE_CONCURRENT);
/// 创建异步线程
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"并发异步1 %@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"并发异步2 %@",[NSThread currentThread]);
}
});
输出结果在不同子线程,无序执行
并发异步1 {number = 5, name = (null)}
并发异步2 {number = 3, name = (null)}
并发异步2 {number = 3, name = (null)}
并发异步1 {number = 5, name = (null)}
- 主队列同步
/// 获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
/// 创建同步线程
dispatch_sync(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"主队列同步1 %@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"主队列同步2 %@",[NSThread currentThread]);
}
});
程序崩溃,原因是把任务放到主线程队列中,它就会立即执行,而主线程正在处理syncMain方法,任务需要等待主队列执行完才能执行,syncMain执行到第一个任务时,又要等待第一个任务完成才能继续执行,这样就形成了死锁。
- 主队列异步
/// 获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
/// 创建异步线程
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"主队列异步1 %@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"主队列异步2 %@",[NSThread currentThread]);
}
});
输出结果在主线程,顺序执行。
主队列异步1 {number = 1, name = main}
主队列异步1 {number = 1, name = main}
主队列异步2 {number = 1, name = main}
主队列异步2 {number = 1, name = main}
- GCD线程之间通讯
开发中会经常需要做耗时操作的任务,比如网络请求,图片下载等,当完成耗时操作后需要回到主线程更新UI,就需要用到线程之间的通讯。
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
// 模拟图片下载
NSLog(@"开始下载图片");
[NSThread sleepForTimeInterval:3];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"更新UI");
});
});
会新开辟子线程下载,下载完成回到主线程刷新UI。
- CGD栅栏
当我们需要异步执行任务,而任务又分为两组执行,A执行完成之后才能执行B,这时候就可以用到GCD的栅栏dispatch_barrier_async
/// 创建并发队列
dispatch_queue_t queue = dispatch_queue_create("concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"栅栏异步1-%d %@",i,[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"栅栏异步2-%d %@",i,[NSThread currentThread]);
}
});
dispatch_barrier_async(queue, ^{
NSLog(@"并发异步执行");
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"栅栏异步3-%d %@",i,[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; i++) {
NSLog(@"栅栏异步4-%d %@",i,[NSThread currentThread]);
}
});
运行结果:开启多条线程,所有任务并发执行,但3,4在1,2之后才会执行
栅栏异步1-0 {number = 7, name = (null)}
栅栏异步2-0 {number = 6, name = (null)}
栅栏异步1-1 {number = 7, name = (null)}
栅栏异步2-1 {number = 6, name = (null)}
并发异步执行
栅栏异步4-0 {number = 7, name = (null)}
栅栏异步3-0 {number = 6, name = (null)}
栅栏异步3-1 {number = 6, name = (null)}
栅栏异步4-1 {number = 7, name = (null)}
- GCD延时执行
当我们需要等待一段时间后才执行某个任务时就会用到延时执行dispatch_after
NSLog(@"开始");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 4), dispatch_get_main_queue(), ^{
NSLog(@"4秒后执行");
});
- GCD队列组
当我们需要异步执行多个任务,且当所有任务完成后需要做某些操作,就需要用到队列组。
/// 创建一个队列组
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"耗时操作1 %@",[NSThread currentThread]);
});
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"耗时操作2 %@",[NSThread currentThread]);
});
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"耗时操作3 %@",[NSThread currentThread]);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"主线程刷新 %@",[NSThread currentThread]);
});
运行结果为
耗时操作3 {number = 7, name = (null)}
耗时操作2 {number = 5, name = (null)}
耗时操作1 {number = 4, name = (null)}
主线程刷新 {number = 1, name = main}
- GCD对循环任务的处理
/// GCD循环任务处理
dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t i) {
NSLog(@"GCD循环任务处理 %@:%zu",[NSThread currentThread],i);
});
输出结果是由不同线程完成的,有效的提升了循环的效率
GCD循环任务处理 {number = 3, name = (null)}:2
GCD循环任务处理 {number = 6, name = (null)}:1
GCD循环任务处理 {number = 1, name = main}:0
GCD循环任务处理 {number = 5, name = (null)}:3
GCD循环任务处理 {number = 3, name = (null)}:4
GCD循环任务处理 {number = 1, name = main}:5
GCD循环任务处理 {number = 6, name = (null)}:6
GCD循环任务处理 {number = 5, name = (null)}:7
GCD循环任务处理 {number = 3, name = (null)}:8
GCD循环任务处理 {number = 1, name = main}:9
- GCD中的消息与信号
GCD框架中提供了dispatch_source_t的对象,用来接收和传递某个消息,之后执行对应的代码块
/// 创建数据对象
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());
/// 接收数据变化
dispatch_source_set_event_handler(source, ^{
NSLog(@"收到数据 %lu",dispatch_source_get_data(source));
});
/// 启动
dispatch_resume(source);
/// 设置数据
dispatch_source_merge_data(source, 1);
输出:
收到数据 1
GCD还有一个概念是信号量,它的用法与消息类似
/// 创建一个信号 初始值为0
dispatch_semaphore_t singer = dispatch_semaphore_create(0);
/// 发送信号
dispatch_semaphore_signal(singer);
/// 等待信号,当信号大于0执行后面的代码,否则等待(会阻塞当前线程),第二个参数为等待时间,DISPATCH_TIME_FOREVER为一直等待
dispatch_semaphore_wait(singer, DISPATCH_TIME_FOREVER);
NSLog(@"执行");
注:每次执行过等待信号后,信号量会减1
NSOperation
NSOperation可以理解为任务操作,是基于Objective-C封装的一套管理与执行线程操作的类,这个类是一个抽象类,通常我们会使用NSInvocationOperation和NSBlockOperation这两个子类进行开发,或者也可以继承于NSOperation类,封装我们自己的类。
- NSInvocationOperation
/// 创建NSInvocationOperation
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
/// 启动NSInvocationOperation
[invocationOperation start];
输出结果:并未开启新的线程
当前线程 {number = 1, name = main}
- NSBlockOperation
/// NSBlockOperation
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"当前线程 %@",[NSThread currentThread]);
}];
/// 启动NSBlockOperation
[operation start];
输出结果:并未开启新的线程
当前线程 {number = 1, name = main}
直接使用NSInvocationOperation和NSBlockOperation不会开启新的线程,但是可以使用NSBlockOperation的addExecutionBlock方法开启新的线程
/// NSBlockOperation
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"当前线程 %@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"方法一当前线程 %@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"方法二当前线程 %@",[NSThread currentThread]);
}];
/// 启动NSBlockOperation
[operation start];
输出结果:通过addExecutionBlock的方法开启了新的线程
当前线程 {number = 1, name = main}
方法一当前线程 {number = 8, name = (null)}
方法二当前线程 {number = 7, name = (null)}
- 继承NSOperation的子类
首先我们继承NSOperation,然后重新它的main方法
#import "MyOperation.h"
@implementation MyOperation
- (void)main{
NSLog(@"当前线程%@",[NSThread currentThread]);
}
@end
使用:
/// 创建自定义的Operation
MyOperation *operation = [[MyOperation alloc] init];
[operation start];
结果:并未开启新的线程
当前线程{number = 1, name = main}
总结:NSOperation是需要配合使用NSOperationQueue来实现多线程的
- NSOperationQueue
NSOperationQueue有两种队列(主队列、其他队列)
/// 主队列
NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];
/// 其他队列,默认并发,开启多线程
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSOperationQueue有一个参数maxConcurrentOperationCount最大并发数量,默认为-1,并发执行,当maxConcurrentOperationCount为1时,则表示串行执行,当maxConcurrentOperationCount大于1时,则表示并发执行。
- NSOperation+NSOperationQueue
创建NSInvocationOperation和NSBlockOperation,并把它们加入队列。
/// 创建队列,默认并发执行
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
/// 创建NSInvocationOperation
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
/// NSBlockOperation
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"当前线程 %@",[NSThread currentThread]);
}];
[queue addOperation:invocationOperation];
[queue addOperation:operation];
结果:都是在子线程执行的,开启了新线程。
当前线程 {number = 5, name = (null)}
当前线程 {number = 6, name = (null)}
直接加入队列
/// 创建队列,默认并发执行
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSLog(@"当前线程 %@",[NSThread currentThread]);
}];
结果:子线程执行,开启了新线程。
当前线程 {number = 7, name = (null)}
NSOperationQueue的串行与并行
/// 创建队列,默认并发执行
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
/// 设置最大并发数
queue.maxConcurrentOperationCount = 1;
[queue addOperationWithBlock:^{
NSLog(@"当前线程1 %@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"当前线程2 %@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
NSLog(@"当前线程3 %@",[NSThread currentThread]);
}];
当maxConcurrentOperationCount设置为1时,开启了新的线程且串行执行。
当前线程1 {number = 6, name = (null)}
当前线程2 {number = 6, name = (null)}
当前线程3 {number = 6, name = (null)}
当maxConcurrentOperationCount大于1时,开启了新的线程且并发执行。
当前线程2 {number = 6, name = (null)}
当前线程1 {number = 4, name = (null)}
当前线程3 {number = 5, name = (null)}
- NSOperation操作依赖
当我们在执行线程任务时,B需要等待A执行完成之后才能执行,这时候就可以使用NSOperation的操作依赖。
/// 创建队列,默认并发执行
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
/// 操作1
NSInvocationOperation *invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationOperation) object:nil];
/// 操作2
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@" 操作2当前线程 %@",[NSThread currentThread]);
}];
/// 操作1需要等待操作2执行完成
[invocationOperation addDependency:operation];
[queue addOperation:invocationOperation];
[queue addOperation:operation];
结果:操作2完成之后才会执行操作1
操作2当前线程 {number = 7, name = (null)}
操作1当前线程 {number = 7, name = (null)}