1.多线程概念
- 1.什么是多线程
事实上,同一时间内单核的CPU只能执行一个线程,多线程是CPU快速的在多个线程之间进行切换(调度),造成了多个线程同时执行的假象。
如果是多核CPU就真的可以同时处理多个线程了。 - 2.为什么要用多线程
多线程的目的是为了同步完成多项任务,通过提高系统的资源利用率来提高系统的效率 - 3.多线程的优缺点
1.优点
能适当提高程序的运行效率
能适当提高资源利用率(CPU、内存利用率)
2.缺点
开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能
线程越多,CPU在调度线程上的开销就越大
程序设计更加复杂:比如线程之间的通信、多线程的数据共享
2.多线程的实现方式
(1)pthread(POSIX Threads):一套C语言编写的跨平台多线程API,使用难度大,需要手动管理线程生命周期。
(2)NSThread:面向对象操作线程,使用相对简单,需要手动管理线程生命周期。
(3)GCD:苹果多核编程解决方案,使用起来非常方便。需要自己实现如:限制并发数,任务间的依赖等功能。自动管理线程生命周期。
(4)NSOperation:基于GCD的封装,面向对象操作线程,提供了比GCD更丰富的API:限制最大并发数,设置任务依赖关系。但是它不能直接使用,因为它是一个抽象类,可以继承它或者使用系统定义NSInvocationOperation或NSBlockOperation。自动管理线程生命周期。
3.GCD
任务:任务就是执行操作,指的是block中的代码
同步执行(sync):是指不具备开辟新线程的能力
异步执行(async):不会阻塞主线程,会开启新线程执行任务,在后台执行队列:
这里的队列就是任务队列,即用来存放任务的队列。队列是一种特殊的线性表,采用先进先出(FIFO)的原则,
每次新任务都会被插入到队列尾部,而执行队列中的任务时,会从队列头部开始读取并执行。GCD中有两种队列:串行队列和并行队列
1.并行队列(DISPATCH_QUEUE_CONCURRENT):可以多个任务同时进行,也就会开启多个线程执行任务。交替执行。
2.串行队列(DISPATCH_QUEUE_SERIAL):任务一个接着一个执行,也就是一个任务执行完后,下一个任务就开始。一个接着一个执行。特殊队列
1.全局队列 dispatch_get_global_queue ,全局队列就是并行队列,供整个应用使用;需要两个参数,第一个是队列优先级(DISPATCH_QUEUE_PRIORITY_DEFAULT),第二个0即可(官方文档说:For future use)
2.主队列 dispatch_get_main_queue ,主队列就是串行队列,在应用启动时,就创建好了,所以我们要用的时候就直接拿来用而不需要创建任务队列组合情况
1.异步 + 串行 可以开辟新线程,但是任务只能一个一个取,所以没必要开辟新线程 结果:单线程
2.异步 + 并行 可以开辟多线程,前一个任务一旦执行,就可以调度下一个任务 结果 :多线程
3.同步 + 串行 即不可以开辟新线程,也不可以不等待前一任务完成就调度,完全没必要开辟新线程 结果: 单线程
4.同步 + 并行 不可以开辟新线程,但是可以前一个任务一旦执行,就可以调度下一个任务 结果:单线程
5.同步+主队列 死锁
6.异步+主队列 只在主线程中执行任务,执行完一个任务,再执行下一个任务-
三种死锁情况(队列阻塞)
1.主线程主队列嵌套同步队列
2.同步串行嵌套同步串行任务
3.异步串行嵌套同步串行任务3.1GCD常用方法
- 1.栅栏函数
dispatch_barrier_sync(myconcurrent, ^{ }); 作用相同,但是这个会堵塞线程,影响后面的执行
dispatch_barrier_async(myconcurrent, ^{ }); 前面的任务执行完毕,才会来到这里,不影响后面任务的执行
- 1.栅栏函数
dispatch_queue_t queue = dispatch_queue_create("testqueue", DISPATCH_QUEUE_CONCURRENT);
NSLog(@"start");
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_barrier_sync(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
NSLog(@"center");
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
NSLog(@"end");
//结果 12结束后再执行34
//dispatch_barrier_async和dispatch_barrier_sync区别 (都是在栅栏函数之前的先执行,在栅栏后面的后执行)
//dispatch_barrier_async不会阻塞当前线程block添加到queue中后就会立即返回执行线程中后面的方法
//dispatch_barrier_sync会阻塞当前线程
- 2.GCD延时方法
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ }); - 3.只执行一次操作(单例创建)
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{}); - 4.快速迭代方法 (同时遍历)
dispatch_queue_t global_queue = dispatch_get_global_queue(0, 0);
dispatch_apply(6, global_queue, ^(size_t index) {
NSLog(@"%zd %@", index, [NSThread currentThread]);
}); - 5.队列组dispatch_group_t
第一种 会阻塞主线程,等待上面的任务执行完,再继续向下执行
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
第二种 不会阻塞主线程,等待上面的任务执行完,该block就会执行 (推荐)
dispatch_group_notify(group, dispatch_get_main_queue(), ^{ NSLog(@"回到主线程 %@", [NSThread currentThread]); }); - 6.信号量dispatch_semaphore_t
dispatch_semaphore_wait当信号量的值大于0时,信号量会减1,当信号量的值等于0时,阻塞线程直到该信号量的值大于0或者达到等待时间
dispatch_semaphore_signal释放信号量,使得该信号量的值加1
作用
实现异步任务同步执行
限制最大并发数
dispatch_semaphore_t sem = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
sleep(3);
dispatch_semaphore_signal(sem);
NSLog(@"A --thread:%@", [NSThread currentThread]);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
sleep(3);
dispatch_semaphore_signal(sem);
NSLog(@"B --thread:%@", [NSThread currentThread]);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
dispatch_async(queue, ^{
sleep(3);
NSLog(@"C --thread:%@", [NSThread currentThread]);
dispatch_semaphore_signal(sem);
});
dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
4.NSOperation
- 1.什么是NSOperation
1.NSOperation是Apple提供给开发者的一套多线程解决方案,实际上是基于GCD的一套更高级封装,完全Objective-C代码。简单、易用、代码可读性高。
2.NSOperation需要配合NSOperationQueue来实现多线程,因为默认情况下NSOperation单独使用时是系统同步执行操作,并没有开启新线程的能力,只有配合NSOperationQueue才能实现异步执行
3.因为NSOperation是基于GCD的,那么使用起来也和GCD差不多,其中,NSOperation相当于GCD中的任务,而NSOperationQueue则相当于GCD中的队列。 - 2.NSOperation实现多线程的使用步骤分为三步:
1.创建任务 NSBlockOperation/NSInvocationOperation
2.创建队列 NSOperationQueue
主队列NSOperationQueue *queue = [NSOperationQueue mainQueue];
其它队列NSOperationQueue *queue = [[NSOperationQueue alloc] init];根据maxConcurrentOperationCount是否串行 1串行 >1并发默认并发 - 3.操作依赖
// 1.创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.创建操作
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
}
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印当前线程
}
}];
// 3.添加依赖
[op2 addDependency:op1]; // 让op2 依赖于 op1,则先执行op1,在执行op2
// 4.添加操作到队列中
[queue addOperation:op1];
[queue addOperation:op2];