iOS开发之多线程
iOS开发之多线程本文章博主和大家一块学习多线程,很自然就涉及到线程和进程,然后涵括NSThread、GCD、NSOperation!然后就是最牛叉的RunLoop和Runtime。一、线程与进程进程是指在系统中正在运行的一个应用程序,就是一段程序的执行过程,每个进程之间是相互独立的。一个进程要想执行任务,必须要有线程,至少有一条线程,一个进程的所有任务都是在线程中执行。其实应用程序启动的时候我么
iOS开发之多线程
本文章博主和大家一块学习多线程,很自然就涉及到线程和进程,然后涵括NSThread、GCD、NSOperation!然后就是最牛叉的RunLoop和Runtime。
一、线程与进程
进程是对运行时程序的封装,是系统进行资源调度和分配的基本单位,实现了操作系统的并发。
线程是进程的子任务,是CPU调度和分配的基本单位,用于保障程序执行的实时性,实现进程内部的并发。
区别:
- 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。
- 进程是资源分配的最小单位,线程是CPU调度的最小单位
- 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。
- 进程间不会相互影响 ;线程:一个线程挂掉将导致整个进程挂掉
- 线程之间的通信更加方便,同一个进程下线程共享全局变量,静态变量等数据。
二、多线程
多线程的优点和缺点
- 优点
1.能适当的提高程序的执行效率
2.能适当提高资源利用率(CPU内存利用率) - 缺点
1.开启线程需要占用一定的内存空间,如果开启大量的线程,则会占用大量的内存空间,降低程序的性能。
2.线程越多,CUP在调度线程的开销就越大。
3.程序设计更加复杂,比如线程之间的通信,多线程的数据共享。
多线程的应用
1.使用单利模式时,可以使用GCD
2.耗时操作放入子线程处理,完成后回主线程显示。
3.从数据库读取大量数据,可开辟子线程操作。
4.处理音频、视频数据时,在子线程处理。
5.数据同步操作。
三、容易混淆的4个术语
同步和异步主要影响:能不能开启新的线程。
1.同步:只是在当前线程中执行任务,不具备开启新线程的能力。
2.异步:可以在新的线程中执行,具备开启新线程的能力。
并发和串行主要影响:任务的执行方式
并发:多个任务并发(同时)执行。
串行:一个任务执行完毕后,再执行下一个任务。
四、NSThread、NSOperation、GCD
1.NSThread
1)通过NSThread的对象方法(动态创建)
2)通过NSThread的类方法(静态创建)
NSThread的线程管理
- 线程创建
- 线程开启
- 线程取消
- 线程关闭
- 线程暂停
- 设置线程优先级
NSThread的线程通信
- 指定当前线程执行操作
- (在其他线程中)指定主线程执行操作
- 在主线程中)指定其他线程执行操作
2.NSOperation
2.1 NSOperation 基本使用
在iOS开发中,为了提升用户体验,我们通常会将操作耗时的操作放在主线程之外的线程进行处理。对于正常的简单操作,我们更多的选择代码更少的GCD,让我们专注于自己的业务逻辑开发。NSOperation在iOS4后也基于GCD实现,但是对于GCD来说可控性更强,并且可以加入操作依赖。
(1) 相关概念
NSOperation是对GCD的封装,其中有两个核心概念【队列+操作】
1.NSOperation本身是抽象类,只能有它的子类。
2.两大子类分别是:NSBlockOperation、NSInvocationOperation,当然你也可以自定义继承自NSOperation的类。
(2)基本使用
- 实例化NSOperation的子类,绑定执行的操作。
- 创建NSOperationQueue队列,将NSOperation实例添加进来。
- 系统会自动将NSOperationQueue队列中检测取出和执行NSOperation的操作。
NSInvocationOperation类
// 01 NSInvocationOperation创建线程
/*
第一个参数:目标对象
第二个参数:该操作要调用的方法,最多接受一个参数
第三个参数:调用方法传递的参数,如果方法不接受参数,那么该值传nil
*/
- (void) method1 {
NSString * imageUrl = @"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=482494819,3150422682&fm=200&gp=0.jpg";
NSInvocationOperation * invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadImageSource:) object:imageUrl];
// [invocationOperation start];
//一旦执行操作,就会调用target的sel方法, 默认情况下调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作,只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue addOperation:invocationOperation];
}
//更新imageView
- (void)updateImage:(NSData *)data{
//在主线程中更新UI
//将二进制数据转换为图片
UIImage *image=[UIImage imageWithData:data];
//设置image
self.imageView.image=image;
}
NSBlockOperation类
- 创建NSBlockOperation对象
- 通过addExecutionBlock:方法添加更多的操作
NSOperationQueue 控制串行执行、并发执行
使用NSOperation Queue创建的自定义队列同时具有串行、并发执行的能力,这里的关键就是maxConcurrentOperationCount这个关键属性,叫做最大并发操作数,用来控制一个特定队列中可以有多少个操作同时参与并发执行。 - maxConcurrentOperationCount 默认情况下为-1,表示不进行限制,可进行并发执行。
- maxConcurrentOperationCount 为1时,队列为串行队列。只能串行执行。
- maxConcurrentOperationCount 大于1时,队列为并发队列。
队列的取消、暂停、和恢复
// 恢复队列,继续执行
// self.queue.suspended = NO;
// 暂停(挂起)队列,暂停执行
// self.queue.suspended = YES;
// 取消队列的所有操作
[self.queue cancelAllOperations];
操作依赖
NSOperation之间可以设置依赖来保证执行顺序
不添加依赖之前op1、op2、op3的顺序是随机的
在不同queue的NSOperation之间创建依赖关系
在不加依赖之前,op1和op2的执行顺序是随机的,添加依赖后op2会在op1之后执行。
线程间的通讯
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self updateImage:data];
}];
3.GCD
3.1基本概念
全称是Grand Central Dispatch,即:强大的中枢调度器,它是纯C语言的,提供了非常多强大的函数。
3.2GCD的优势:
GCD是苹果公司为多核的并行运算提出的解决方案
GCD会自动利用更多的CPU内核(比如双核、四核)
GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码
3.3GCD的使用步骤:
-
指定任务:确定想要做的事
-
将任务添加到队列中:GCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则:先进先出,后进后出。
3.4GCD的队列
3.4.1 GCD的队列可以分为2大类型:
1.并发队列(Current Dispatch Queue)
2.串行队列(Serial Dispatch Queue)
3.4.2 使用dispatch_queue_create函数创建队列
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
其中:const char *label 列队列的名称
dispatch_queue_attr_t attr 队列的类型
创建一个并发队列
dispatch_queue_t queue1 = dispatch_queue_create("myQueue1", DISPATCH_QUEUE_CONCURRENT);
创建一个串行队列
dispatch_queue_t queue2 = dispatch_queue_create("myQueue2", DISPATCH_QUEUE_SERIAL);
GCD默认意境提供了全局的并发队列,供整个应用使用,可以无需手动创建
使用dispatch_get_global_queue 函数获得全局并发队列
//获得全局的并发队列
dispatch_queue_t queue3 = 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 --》 后台
第二个参数标记:是为了未来使用保留的!所以这个参数应该永远指定为0
GCD中获得串行有2种途径
1.使用dispatch_queue_create 创建串行队列,创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)
dispatch_queue_t queue4 = dispatch_queue_create("com.520.queue", NULL);
2.使用主队列 (跟主线程相关联的队列),主队列时GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放在主线程中执行,dispatch_get_main_queue()获得主队列
dispatch_queue_t queue5 = dispatch_get_main_queue();
3.4.3 应用
3.4.3.1 串行队里
3.4.3.2.并发队列(Concurrent Dispatch Queue)
//
// ViewController.m
// TestThread
//
// Created by denggaoqiang on 2022/9/10.
//
#import "ViewController.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self example14];
}
- (void)example1 {
dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"当前同步任务1一-------%@",[NSThread currentThread]);
sleep(3);
});
NSLog(@"1111");
dispatch_sync(queue, ^{
NSLog(@"当前同步任务2一-------%@",[NSThread currentThread]);
sleep(2);
});
NSLog(@"2222");
dispatch_sync(queue, ^{
NSLog(@"当前同步任务3一-------%@",[NSThread currentThread]);
sleep(1);
});
NSLog(@"走到这里了");
/** 打印结果
当前同步任务1
1111
当前同步任务2
2222
当前同步任务3
走到这里了
*/
}
- (void)example2 {
dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
sleep(5);
NSLog(@"当前异步任务1一-------%@",[NSThread currentThread]);
});
NSLog(@"1111");
dispatch_async(queue, ^{
sleep(2);
NSLog(@"当前异步任务2一-------%@",[NSThread currentThread]);
});
NSLog(@"2222");
dispatch_async(queue, ^{
sleep(1);
NSLog(@"当前异步任务3一-------%@",[NSThread currentThread]);
});
NSLog(@"走到这里了");
/** 打印结果
1111
2222
走到这里了
当前异步任务1
当前异步任务2
当前异步任务3
*/
}
- (void)example3 {
dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
sleep(3);
NSLog(@"当前同步任务1一-------%@",[NSThread currentThread]);
});
NSLog(@"1111");
dispatch_async(queue, ^{
NSLog(@"当前异步任务2开始一-------%@",[NSThread currentThread]);
sleep(10);
NSLog(@"当前异步任务2结束一-------%@",[NSThread currentThread]);
});
NSLog(@"2222");
dispatch_sync(queue, ^{
sleep(2);
NSLog(@"当前同步任务3一-------%@",[NSThread currentThread]);
});
NSLog(@"3333");
dispatch_async(queue, ^{
sleep(1);
NSLog(@"当前异步任务4一-------%@",[NSThread currentThread]);
});
NSLog(@"走到这里了");
/** 打印结果
当前同步任务1
1111
2222
当前异步任务2开始
当前异步任务2结束
当前同步任务3
3333
走到这里了
当前异步任务4
*/
}
- (void)example4 {
dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
sleep(5);
NSLog(@"当前同步任务1一-------%@",[NSThread currentThread]);
});
NSLog(@"1111");
dispatch_sync(queue, ^{
sleep(3);
NSLog(@"当前同步任务2一-------%@",[NSThread currentThread]);
});
NSLog(@"2222");
dispatch_sync(queue, ^{
sleep(1);
NSLog(@"当前同步任务3一-------%@",[NSThread currentThread]);
});
NSLog(@"走到这里了");
/** 打印结果
当前同步任务1
1111
当前同步任务2
2222
当前同步任务3
走到这里了
*/
}
- (void)example5 {
dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
sleep(5);
NSLog(@"当前同步任务1一-------%@",[NSThread currentThread]);
});
NSLog(@"1111");
dispatch_async(queue, ^{
sleep(8);
NSLog(@"当前异步任务2一-------%@",[NSThread currentThread]);
});
NSLog(@"2222");
dispatch_async(queue, ^{
sleep(3);
NSLog(@"当前同步任务3一-------%@",[NSThread currentThread]);
});
NSLog(@"3333");
dispatch_async(queue, ^{
sleep(1);
NSLog(@"当前异步任务4一-------%@",[NSThread currentThread]);
});
NSLog(@"走到这里了");
/** 打印结果
1111
2222
3333
走到这里了
当前异步任务4
当前异步任务3
当前异步任务1
当前异步任务2
*/
}
- (void)example6 {
dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
sleep(3);
NSLog(@"当前同步任务1一-------%@",[NSThread currentThread]);
});
NSLog(@"1111");
dispatch_async(queue, ^{
sleep(8);
NSLog(@"当前异步任务2一-------%@",[NSThread currentThread]);
});
NSLog(@"2222");
dispatch_sync(queue, ^{
sleep(3);
NSLog(@"当前同步任务3一-------%@",[NSThread currentThread]);
});
NSLog(@"3333");
dispatch_async(queue, ^{
sleep(1);
NSLog(@"当前异步任务4一-------%@",[NSThread currentThread]);
});
NSLog(@"走到这里了");
/** 打印结果
当前同步任务1
1111
2222
当前同步任务3
3333
走到这里了
当前异步任务4
当前异步任务2
*/
}
/**总结:
一、在同一串行队列种
同步里面创建同步 死锁崩溃
同步里面创建异步 正常
异步里面创建同步 死锁崩溃
异步里面创建异步 正常
二、在同一并发队列
同步里面创建同步 正常
同步里面创建异步 正常
异步里面创建同步 正常
异步里面创建异步 正常
*/
-(void)example71 {
//串行队列 同步里面创建同步 死锁崩溃
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"任务1...");
NSLog(@"%@", [NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"任务2...");
NSLog(@"%@", [NSThread currentThread]);
});
NSLog(@"走到这里了");
});
NSLog(@"结束");
}
-(void)example72 {
//串行队列 同步里面创建异步 正常
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
NSLog(@"任务1...");
NSLog(@"%@", [NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"任务2...");
NSLog(@"%@", [NSThread currentThread]);
});
NSLog(@"走到这里了");
});
NSLog(@"结束");
}
-(void)example73 {
//串行队列 异步里面创建同步 死锁崩溃
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"任务1...");
NSLog(@"%@", [NSThread currentThread]);
dispatch_sync(queue, ^{
NSLog(@"任务2...");
NSLog(@"%@", [NSThread currentThread]);
});
NSLog(@"走到这里了");
});
NSLog(@"结束");
}
-(void)example74 {
//串行队列 异步里面创建异步 正常
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
NSLog(@"任务1...");
NSLog(@"%@", [NSThread currentThread]);
dispatch_async(queue, ^{
NSLog(@"任务2...");
NSLog(@"%@", [NSThread currentThread]);
});
NSLog(@"走到这里了");
});
NSLog(@"结束");
}
-(void)example81 {
//并发队列 同步里面创建同步 正常
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
sleep(3);
NSLog(@"任务1...");
NSLog(@"%@", [NSThread currentThread]);
dispatch_sync(queue, ^{
sleep(1);
NSLog(@"任务2...");
NSLog(@"%@", [NSThread currentThread]);
});
NSLog(@"走到这里了");
});
NSLog(@"结束");
}
-(void)example82 {
//并发队列 同步里面创建异步 正常
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
sleep(3);
NSLog(@"任务1...");
NSLog(@"%@", [NSThread currentThread]);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"任务2...");
NSLog(@"%@", [NSThread currentThread]);
NSLog(@"走到这里了");
});
});
NSLog(@"结束");
}
-(void)example83 {
//并发队列 异步里面创建同步 正常
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
sleep(3);
NSLog(@"任务1...");
NSLog(@"%@", [NSThread currentThread]);
dispatch_sync(queue, ^{
sleep(1);
NSLog(@"任务2...");
NSLog(@"%@", [NSThread currentThread]);
});
NSLog(@"走到这里了");
});
NSLog(@"结束");
}
-(void)example84 {
//并发队列 异步里面创建异步 正常
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
sleep(3);
NSLog(@"任务1...");
NSLog(@"%@", [NSThread currentThread]);
dispatch_async(queue, ^{
sleep(1);
NSLog(@"任务2...");
NSLog(@"%@", [NSThread currentThread]);
NSLog(@"走到这里了");
});
});
NSLog(@"结束");
}
-(void)example91{
//任务1和任务2在同一个线程里,在不同的队列里面,可以同步执行 运行正常
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue1, ^{
NSLog(@"%@", [NSThread currentThread]);
NSLog(@"任务1...");
dispatch_sync(queue2, ^{
NSLog(@"%@", [NSThread currentThread]);
NSLog(@"任务2...");
});
NSLog(@"走到这里了");
});
NSLog(@"结束");
}
-(void)example92{
//任务1和任务2在两个不同线程里,并且两个不同队列,不会造成死锁 可以正常运行
dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue1, ^{
NSLog(@"%@", [NSThread currentThread]);
NSLog(@"任务1...");
dispatch_async(queue2, ^{
NSLog(@"%@", [NSThread currentThread]);
NSLog(@"任务2...");
});
NSLog(@"走到这里了");
});
NSLog(@"结束");
}
-(void)example10 {
//异步串行group
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.sccc", DISPATCH_QUEUE_SERIAL);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[self doSomething:^() {
NSLog(@"任务一");
}];
});
dispatch_group_async(group, queue, ^{
[self doSomething:^() {
NSLog(@"任务二");
}];
});
dispatch_group_async(group, queue, ^{
[self doSomething:^() {
NSLog(@"任务三");
}];
});
dispatch_group_notify(group, queue, ^{
NSLog(@"前面的任务已完成");
});
}
-(void)example11 {
//异步串行group
//创建串行队列
dispatch_queue_t queue = dispatch_queue_create("com.sccc", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
[self doSomething:^() {
NSLog(@"任务一");
}];
});
dispatch_group_async(group, queue, ^{
sleep(3);
[self doSomething:^() {
NSLog(@"任务二");
}];
});
dispatch_group_async(group, queue, ^{
[self doSomething:^() {
NSLog(@"任务三");
}];
});
dispatch_group_notify(group, queue, ^{
NSLog(@"前面的任务已完成");
});
}
- (void)doSomething:(void (^)(void))handler {
if (handler) {
sleep(2);
handler();
}
}
-(void)example12 {
// 因为 dispatch_group_async 里面的任务是异步的,所以任务在执行的时候,它不会去等待 for 循环执行结束,它会直接跳过 dispatch_async 这 block 执行下一句去了,所以 dispatch_group_notify 也会很快就执行。
// 创建一个group
dispatch_group_t group = dispatch_group_create();
// 创建一个队列:全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 将任务1添加到 group 中
dispatch_group_async(group, queue, ^{
// 模拟异步网络请求
sleep(5);
NSLog(@"任务一完成");
});
// 将任务2添加到 group 中
dispatch_group_async(group, queue, ^{
// 模拟异步网络请求
sleep(3);
NSLog(@"任务二完成");
});
// 任务1和任务2执行结束,回调
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"完成任务");
});
});
}
-(void)example13 {
// 创建一个group
dispatch_group_t group = dispatch_group_create();
// 创建一个队列:全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 进入group
dispatch_group_enter(group);
// 模拟异步网络请求
dispatch_async(queue, ^{
sleep(3);
// 离开group
NSLog(@"任务一完成");
dispatch_group_leave(group);
});
// 进入group
dispatch_group_enter(group);
// 模拟异步网络请求
dispatch_async(queue, ^{
sleep(4);
// 离开group
NSLog(@"任务二完成");
dispatch_group_leave(group);
});
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"完成任务");
});
});
}
-(void)example14{
// 创建一个group
dispatch_group_t group = dispatch_group_create();
// 创建一个队列:全局队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 创建信号量,并且设置值为0
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// 将任务1添加到 group 中
dispatch_group_async(group, queue, ^{
// 模拟异步网络请求
dispatch_async(queue, ^{
sleep(3);
NSLog(@"任务一完成");
// 每次发送信号则 semaphore 会 +1
dispatch_semaphore_signal(semaphore);
});
// 由于是异步执行的,当 semaphore 等于 0,则会阻塞当前线程,直到执行了 block 的 dispatch_semaphore_signal,semaphore + 1,才会继续执行。
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
// 将任务2添加到 group 中
dispatch_group_async(group, queue, ^{
// 模拟异步网络请求
dispatch_async(queue, ^{
sleep(3);
NSLog(@"任务二完成");
dispatch_semaphore_signal(semaphore);
});
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
});
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"完成任务");
});
});
}
@end
面试题1
- (void)wbinterDemo{
dispatch_queue_t queue = dispatch_queue_create("com.lg.cooci.cn", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
sleep(2);
NSLog(@"1");
});
dispatch_async(queue, ^{
NSLog(@"2");
});
NSLog(@"00");
dispatch_sync(queue, ^{ NSLog(@"3"); });
NSLog(@"0");
dispatch_async(queue, ^{
NSLog(@"7");
});
dispatch_async(queue, ^{
NSLog(@"8");
});
dispatch_async(queue, ^{
NSLog(@"9");
});
//打印顺序00 1 2 3 0 7 8 9
}
面试题2
- (void)MTDemo{
self.num = 0;
while (self.num < 50) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.num++;
});
}
NSLog(@"end : %ld",(long)self.num);
}
这里输出的结果会是大于等于 50,首先因为这里是 while 循环,所以 self.num 不大于等于 50 的情况下不会执行打印,然后因为是并发队列异步执行,所以这里会开启多条线程,所以就有可能 self.num 刚满足大于等于 50 的条件,跳出循环的时候别的线程正好开始执行任务,然后就会对 self.num 再次进行加加运算,所以最后的打印结果大于等于 50。
面试题3
- (void)KSDemo {
for (int i= 0; i<10000; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.num++;
});
}
NSLog(@"end : %d",self.num);
}
这里因为判断条件是一个新的变量 i,只会循环一万次,然后因为是多线程,所以当跳出循环的时候有的线程任务并没有执行完成,所以最后的打印结果小于等于 10000。
3.4.3.3.线程间的通信
- Direct messaging:这是大家非常熟悉的**-performSelector:系列**。
- Global variables…:直接通过全局变量、共享内存等方式,但这种方式会造成资源抢夺,涉及到线程安全问题。
- Conditions:一种特殊的锁–条件锁,当使用条件锁使一个线程等待(wait)时,该线程会被阻塞并进入休眠状态,在另一个线程中对同一个条件锁发送信号(single),则等待中的线程会被唤醒继续执行任务。
- Run loop sources:通过自定义Run loop sources来实现,后面的文章会单独研究Run loop。
- Ports and sockets:通过端口和套接字来实现线程间通讯。
- Run loop:NSMachPort
3.4.3.4.其他应用
1.延时执行
iOS常见三种延时执行方式
1)调用NSObject的方法
[self performSelector:@selector(需要执行的方法名) withObject:nil afterDelay:2.0]
2)使用GCD函数
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后执行这里的代码..
});
3)使用定时器
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(需要执行的方法名) userInfo:nil repeats:NO];
2.一次性代码
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//里面的代码只会被执行一次
});
3.队列组
有这么一个需求,首先:分别异步执行2个耗时的操作,其次:等2个异步操作都执行完毕后,再回到主线程执行操作,如果想要快速高效地实现上述需求,可以考虑用队列组。
dispatch_group_tgroup = 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(), ^{
// 等前面的异步操作都执行完毕后,回到主线程...
});
4.快速迭代
我只想说速度非常快,要加一个__block因为block代码默认不能改外面的东西
5.barry执行任务函数
在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行。
下面的例子是在添加到队列的任务1、任务2、任务3执行完毕后,然后才执行barrier,barrier执行完毕后才执行任务4、任务5。
dispatch_barrier在并发队列中创建一个同步点,当并发队列中遇到一个 dispatch_barrier时,会延时执行该 dispatch_barrier,等待在 dispatch_barrier之前提交的任务block执行完后才开始执行,之后,并发队列继续执行后续block任务。
6.GCD与定时器
//创建一个定时器(dispatch_source_t本质上还是一个OC对象)
@property (nonatomic,strong) dispatch_source_t gcdTimer;
//定时器设置
-(void)loadTimer{
[self pause];
dispatch_queue_t queue = dispatch_get_main_queue();
/*
第一个参数:dispatch_source_type_t type为设置GCD源方法的类型,前面已经列举过了。
第二个参数:uintptr_t handle Apple的API介绍说,暂时没有使用,传0即可。
第三个参数:unsigned long mask Apple的API介绍说,使用DISPATCH_TIMER_STRICT,会引起电量消耗加剧,毕竟要求精确时间,所以一般传0即可,视业务情况而定。
第四个参数:dispatch_queue_t _Nullable queue 队列,将定时器事件处理的Block提交到哪个队列,可以传Null,默认为全局队列。
*/
self.gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//将定时器写成属性,是因为内存管理的原因,使用了dispatch_source_create方法,这种方法GCD是不会帮你管理内存的。
//设置定时器的各种属性
/*
第二个参数:dispatch_time_t start, 定时器开始时间,类型为 dispatch_time_t,其API的abstract标明可参照dispatch_time()和dispatch_walltime(),同为设置时间,但是后者为“钟表”时间,相对比较准确,所以选择使用后者。dispatch_walltime(const struct timespec *_Nullable when, int64_t delta),参数when可以为Null,默认为获取当前时间,参数delta为增量,即获取当前时间的基础上,增加X秒的时间为开始计时时间,此处传0即可。
第三个参数:uint64_t interval,定时器间隔时长,由业务需求而定。
第四个参数:uint64_t leeway, 允许误差,此处传0即可。
*/
dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0*NSEC_PER_SEC));
uint64_t interval = (uint64_t)(5.0*NSEC_PER_SEC);
dispatch_source_set_timer(self.gcdTimer, start, interval, 0);
//设置回调
__weak typeof(self) weakSelf = self;
dispatch_source_set_event_handler(self.gcdTimer, ^{
//定时器需要执行的操作
[weakSelf timerAction];
});
//启动定时器(默认是暂停)
dispatch_resume(self.gcdTimer);
}
-(void)pause{
if (self.gcdTimer) {
dispatch_cancel(self.gcdTimer);
self.gcdTimer = nil;
}
}
- (void)timerAction
{
// 做你想做的操作
}
关于取消 Timer,另外一个很重要的注意事项,
dispatch_suspend 之后的 Timer,是不能被释放的!下面的代码会引起崩溃:
- (void)stopTimer
{
dispatch_suspend(_gcdTimer);
_gcdTimer = nil; // EXC_BAD_INSTRUCTION 崩溃
}
因此使用 dispatch_suspend 时,Timer 本身的实例需要一直保持。使用 dispatch_source_cancel 则没有这个限制:
- (void)stopTimer
{
dispatch_source_cancel(_gcdTimer);
_gcdTimer = nil; // OK
}
dispatch_suspend 和 dispatch_resume 应该是成对出现的,两者分别会减少和增加 dispatch 对象的挂起计数,但是没有 API 获取当前是挂起还是执行状态,所以需要自己记录,否则会出现Crash。
RunLoop依赖性:GCD定时器不依赖于RunLoop,因此时间更加准确。相比之下,NSTimer依赖于RunLoop,可能会导致定时任务不准时。
使用注意总结:
1、循环引用:因为dispatch_source_set_event_handler回调是个block,在添加到source的链表上时会执行copy并被source强引用,如果block里持有了self,self又持有了source的话,就会引起循环引用。正确的方法是使用weak+strong或者提前调用dispatch_source_cancel取消timer。
2、dispatch_resume和dispatch_suspend调用次数需要平衡,如果重复调用dispatch_resume则会崩溃,因为重复调用会让dispatch_resume代码里if分支不成立,从而执行了DISPATCH_CLIENT_CRASH(“Over-resume of an object”)导致崩溃。
3、source在suspend状态下,如果直接设置source = nil或者重新创建source都会造成crash。正确的方式是在resume状态下调用dispatch_source_cancel(source)释放当前的source。
@interface YourClass ()
{
dispatch_source_t _timer;
BOOL _timerRunning;
}
- (void)startTimer;
- (void)stopTimer;
@end
@implementation YourClass
- (void)startTimer {
if (!_timerRunning) {
_timerRunning = YES;
// 创建一个定时器,这里以5秒为例
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
if (_timer) {
// 设置定时器的开始时间和间隔
uint64_t start = dispatch_walltime(NULL, 0);
uint64_t interval = 5ull * NSEC_PER_SEC;
dispatch_source_set_timer(_timer, start, interval, 0);
// 设置定时器的事件处理块
dispatch_source_set_event_handler(_timer, ^{
// 定时器触发时的操作
NSLog(@"Timer fired!");
});
// 启动定时器
dispatch_resume(_timer);
}
}
}
- (void)stopTimer {
if (_timerRunning) {
_timerRunning = NO;
if (_timer) {
// 停止并释放定时器
dispatch_source_cancel(_timer);
_timer = NULL;
}
}
}
@end
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)