1.多线程相关的几个概念
1.1 任务
任务:就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在GCD中是放在block中的。执行任务有两种方式:同步执行和异步执行。两者的主要区别是是否具备开启新线程的能力。
- 同步:不具备开启新线程的能力,只能在当前线程中执行任务。
- 异步:具备开启新线程的能力,可以在新的线程中执行任务。
注意:异步任务具备开启新线程能力,仅仅表示有这个能力,但是不一定会开启新的线程,下面会详细说明各种情况。
1.2 队列
队列:这里的队列指任务队列,即用来存放任务的队列。队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。在GCD中有两种队列:串行队列和并行队列。
- 串行队列:队列中的任务是一个接一个的执行,按照FIFO的原则,先添加的任务先执行
- 并行队列:队列中的任务可以同时(并行)执行
2.GCD的使用
2.1 获取队列的几种方式
- 获取系统主线程中的主队列(串行队列)
//获取主队列
dispatch_queue_main_t mainQ = dispatch_get_main_queue();
- 获取系统提供的全局队列(并行队列)
//获取全局并行队列
dispatch_queue_global_t globalQ = dispatch_get_global_queue(0, 0);
- 创建串行队列
//创建串行队列
dispatch_queue_t serialQ = dispatch_queue_create("com.example.serial", DISPATCH_QUEUE_SERIAL);
- 创建并行队列
//创建并行队列
dispatch_queue_t concurrentQ = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
2.2 任务创建的方式
- 同步执行
dispatch_queue_global_t queue = dispatch_get_global_queue(0, 0);
dispatch_sync(queue, ^{
// 执行任务的代码写这里
});
- 异步执行
dispatch_queue_global_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
// 执行任务的代码写这里
});
2.3 任务和队列的组合方式
并行队列 | 串行队列 | 主队列 | |
---|---|---|---|
同步执行 | 否 |
否 |
否 |
异步执行 | 可以开启多条新线程 | 只能开启一条新线程 | 否 |
"否"
表示不能开启新线程
代码论证
//获取主队列
dispatch_queue_main_t mainQ = dispatch_get_main_queue();
//创建串行队列
dispatch_queue_t serialQ = dispatch_queue_create("com.example.serial", DISPATCH_QUEUE_SERIAL);
//创建并行队列
dispatch_queue_t concurrentQ = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
- 同步 + 并行
dispatch_sync(concurrentQ, ^{
NSLog(@"---------任务1--------%@",[NSThread currentThread]);
});
dispatch_sync(concurrentQ, ^{
NSLog(@"---------任务2--------%@",[NSThread currentThread]);
});
dispatch_sync(concurrentQ, ^{
NSLog(@"---------任务3--------%@",[NSThread currentThread]);
});
// ---------任务1--------<NSThread: 0x600002b140c0>{number = 1, name = main}
// ---------任务2--------<NSThread: 0x600002b140c0>{number = 1, name = main}
// ---------任务3--------<NSThread: 0x600002b140c0>{number = 1, name = main}
- 同步 + 串行
dispatch_sync(serialQ, ^{
NSLog(@"---------任务1--------%@",[NSThread currentThread]);
});
dispatch_sync(serialQ, ^{
NSLog(@"---------任务2--------%@",[NSThread currentThread]);
});
dispatch_sync(serialQ, ^{
NSLog(@"---------任务3--------%@",[NSThread currentThread]);
});
// ---------任务1--------<NSThread: 0x600002b140c0>{number = 1, name = main}
// ---------任务2--------<NSThread: 0x600002b140c0>{number = 1, name = main}
// ---------任务3--------<NSThread: 0x600002b140c0>{number = 1, name = main}
- 同步 + 主队列
dispatch_async(serialQ, ^{
dispatch_sync(mainQ, ^{
NSLog(@"---------任务1--------%@",[NSThread currentThread]);
});
dispatch_sync(mainQ, ^{
NSLog(@"---------任务2--------%@",[NSThread currentThread]);
});
dispatch_sync(mainQ, ^{
NSLog(@"---------任务3--------%@",[NSThread currentThread]);
});
// ---------任务1--------<NSThread: 0x600002b140c0>{number = 1, name = main}
// ---------任务2--------<NSThread: 0x600002b140c0>{number = 1, name = main}
// ---------任务3--------<NSThread: 0x600002b140c0>{number = 1, name = main}
});
- 异步 + 并行
dispatch_async(concurrentQ, ^{
NSLog(@"---------任务1--------%@",[NSThread currentThread]);
});
dispatch_async(concurrentQ, ^{
NSLog(@"---------任务2--------%@",[NSThread currentThread]);
});
dispatch_async(concurrentQ, ^{
NSLog(@"---------任务3--------%@",[NSThread currentThread]);
});
// ---------任务1--------<NSThread: 0x600000075100>{number = 7, name = (null)}
// ---------任务2--------<NSThread: 0x60000007a800>{number = 3, name = (null)}
// ---------任务3--------<NSThread: 0x600000001ac0>{number = 4, name = (null)}
- 异步 + 串行
dispatch_async(serialQ, ^{
NSLog(@"---------任务1--------%@",[NSThread currentThread]);
});
dispatch_async(serialQ, ^{
NSLog(@"---------任务2--------%@",[NSThread currentThread]);
});
dispatch_async(serialQ, ^{
NSLog(@"---------任务3--------%@",[NSThread currentThread]);
});
// ---------任务1--------<NSThread: 0x6000038f5740>{number = 5, name = (null)}
// ---------任务2--------<NSThread: 0x6000038f5740>{number = 5, name = (null)}
// ---------任务3--------<NSThread: 0x6000038f5740>{number = 5, name = (null)}
- 异步 + 主队列
dispatch_async(mainQ, ^{
NSLog(@"---------任务1--------%@",[NSThread currentThread]);
});
dispatch_async(mainQ, ^{
NSLog(@"---------任务2--------%@",[NSThread currentThread]);
});
dispatch_async(mainQ, ^{
NSLog(@"---------任务3--------%@",[NSThread currentThread]);
});
// ---------任务1--------<NSThread: 0x600001b08ec0>{number = 1, name = main}
// ---------任务2--------<NSThread: 0x600001b08ec0>{number = 1, name = main}
// ---------任务3--------<NSThread: 0x600001b08ec0>{number = 1, name = main}
2.3 GCD的应用
- dispatch_after
用于指定时间之后执行任务
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"3秒钟过后执行");
});
- dispatch_barrier
异步并行执行任务是无序的,有时候需要控制他们的执行顺序,可以使用
dispatch_queue_t queue = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1-------%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"1----❤️-----%d",i);
}
});
dispatch_async(queue, ^{
NSLog(@"2-------%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"2----😁-----%d",i);
}
});
dispatch_barrier_sync(queue, ^{
NSLog(@"3-------%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"3----你的心像一道墙-----%d",i);
}
});
dispatch_async(queue, ^{
NSLog(@"4-------%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"4----🤖-----%d",i);
}
});
- dispatch_group_enter、dispatch_group_leave
当有多个网络请求同时发送的时候,希望所有请求回调都结束再处理一些逻辑,可以使用 dispatch_group_enter、dispatch_group_leave
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
// 当有多个网络请求同时发送的时候,希望所有请求回调都结束再处理一些逻辑,可以使用 dispatch_group_enter、dispatch_group_leave
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i++) {
sleep(1); //模拟异步耗时操作
NSLog(@"async1 ------ %ld ----- %@",i,[NSThread currentThread]);
}
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i++) {
sleep(1); //模拟异步耗时操作
NSLog(@"async2 ------ %ld ----- %@",i,[NSThread currentThread]);
}
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i++) {
sleep(1); //模拟异步耗时操作
NSLog(@"async3 ------ %ld ----- %@",i,[NSThread currentThread]);
}
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"终于到我了 --- %@",[NSThread currentThread]);
});
- dispatch_semaphore_t 信号量
dispatch_semaphore_create(0) 创建一个信号量
dispatch_semaphore_wait 信号量减1
dispatch_semaphore_signal 信号量加1
//创建信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i=0; i<7; i++) {
sleep(1.5);
NSLog(@"----❤️-----%d",i);
}
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
for (int i=0; i<7; i++) {
sleep(1.5);
NSLog(@"----🤩-----%d",i);
}
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
for (int i=0; i<7; i++) {
sleep(1.5);
NSLog(@"----🤖-----%d",i);
}
});
- gcd定时器
注意:要用一个变量引用 timer ,否则会在方法执行完就释放了。
@property (nonatomic, strong) dispatch_source_t timer;
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
self.timer = timer;// @property (nonatomic, strong) dispatch_source_t timer;
// 第二个参数是开始时间,第四个参数是精确度,0表示没有误差
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"123123123");
});
dispatch_resume(timer);
3.多线程常见面试题
3.1 进程和线程的概念?
==进程==: 在iOS中,可以看做一个正在运行的程序,一个app只能有一个进程。
==线程==: 线程是进程的基本执行单元,作用是执行进程中的代码,一个进程最少有一个线程,叫做主线程。
3.2 同步和异步的区别?
同步不具备开启新线程的能力,异步具备开启新线程的能力。
3.3 多线程的原理?
单核CPU同一时刻只能处理1条线程,只有1条线程在工作(执行),多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换),如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象
3.4 什么情况下会造成死锁?
在当前串行队列所在的线程中,同步的向当前串行队列添加一个新的任务,就会产生死锁。
例如: 在主线程中,同步向主队列中添加一个任务就会造成死锁
网友评论