1、关于线程的概念
学习多线程,是为了将一些耗时操作放到后台,以提高用户体验。
-
进程
进程可以理解成是系统中正在运行的一个应用程序。
进程是系统进行资源分配和调度的基本单位。
每个进程是独立的,每个进程均是运行在其专用且受保护的内存空间里。 -
线程
一个进程要想执行任务,必须得有线程(每个进程至少要有一条线程)
线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行
也就是说,进程只是在内存中开辟了一个内存空间中而已,但是线程才是正真执行任务的
线程的串行
如果要在一个线程中执行多个任务,那么只能一个一个的按照顺序来执行这些任务,也就是说在同一时间内,1个线程只能执行一个任务
因此,也可认为线程是进程中的一条执行路径。多线程
1个进程中可以开启多条线程,每条线程可以并发(同时)执行不同的任务 ,多线程可以提高程序的执行效率,尤其是在下载的地方常用。
类比:进程——车间 ,线程——车间工人多线程的原理
同一时间,CPU只能处理一条线程,只有1条线程在工作(执行),但是CPU执行的速度是非常快的,能够快速的在多条线程时间调度(切换),切换到哪个线程,哪个线程就会执行一点。
如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象(其实只是在不同的线程之间切换而已)多线程的优点
能适当提高程序的执行效率
能适当提高资源利用率(CPU、内存利用率)多线程的缺点
开启线程需要占用一定的内存空间(默认情况下,主线程占用1M,子线程占用512KB),如果开启大量的线程,会占用大量的内存空间,降低程序的性能
线程越多,CPU在调度线程上的开销就越大
程序设计更加复杂:比如线程之间的通信、多线程的数据共享什么是主线程
一个iOS程序运行后,默认会开启1条线程,称为“主线程”或“UI线程”主线程的主要作用
显示\刷新UI界面
处理UI事件(比如点击事件、滚动事件、拖拽事件等)主线程的使用注意
别将比较耗时的操作放到主线程中
耗时操作会卡住主线程,严重影响UI的流畅度,给用户一种“卡”的坏体验
2、线程的状态与生命周期
下面分别阐述线程生命周期中的每一步
新建:
实例化线程对象
就绪:
向线程对象发送start消息,线程对象被加入可调度线程池等待CPU调度。
运行:
CPU 负责调度可调度线程池中线程的执行。线程执行完成之前,状态可能会在就绪和运行之间来回切换。就绪和运行之间的状态变化由CPU负责,程序员不能干预。
阻塞:
当满足某个预定条件时,可以使用休眠或锁,阻塞线程执行。sleepForTimeInterval(休眠指定时长),sleepUntilDate(休眠到指定日期),@synchronized(self):(互斥锁)。
死亡:
正常死亡,线程执行完毕。非正常死亡,当满足某个条件后,在线程内部中止执行/在主线程中止线程对象
当死亡后,线程便从内存中消失了,不能重新被调用
线程的exit和cancel
[NSThread exit]:一旦强行终止线程,后续的所有代码都不会被执行。
[thread cancel]取消:并不会直接取消线程,只是给线程对象添加 isCancelled 标记。
注意:
线程的各种状态以及其转换的条件
线程在创建之后,只有在可调度线程池中,才能被调用,
当就绪、运行的状态下,线程存在于可调度线程池
当阻塞状态是,线程存在于内存中
当死亡后,线程便从内存中消失了,不能重新被调用
3、多线程的四种解决方案
-
<1>pthread 的用法
pthread是C语言实现的方案,一般很少用
#import "ViewController.h"
#import <pthread.h>//需要导入头文件
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//使用pthread创建线程 : pthread_create
// pthread_create的参数说明:
//参数1:线程变量
//参数2:线程属性
//参数3:线程要执行的函数(要在这个子线程中执行的任务)
//参数4:执行任务需要的参数
pthread_t threadId;//声明一个线程变量,第一个参数
id str=@"hello"; //第四个参数,id需要转换成void *类型的,因为run方法里面是使用这种类型的参数的,所以使用使用__bridge 侨联
pthread_create(&threadId, NULL, run, (__bridge void *)str);
}
//用作第3个属性参数
void *run(void *prama){ //void类型的不用return,但是void * 是一定要return的,void * 相当于oc 中的id
NSString *str=(__bridge NSString *)(prama);
for(int i=0;i<4;i++){
NSLog(@"%@ %@" ,[NSThread currentThread],str);
}
return NULL;
}
@end
打印结果:
===============================================
pthread[1620:149861] <NSThread: 0x600001ca47c0>{number = 3, name = (null)} hello
pthread[1620:149861] <NSThread: 0x600001ca47c0>{number = 3, name = (null)} hello
pthread[1620:149861] <NSThread: 0x600001ca47c0>{number = 3, name = (null)} hello
pthread[1620:149861] <NSThread: 0x600001ca47c0>{number = 3, name = (null)} hello
number=1表示在主线程,number = 3表示当前线程是子线程
-
<2>NSThread的用法
相较pthread来说,NSThread使用起来更简单快捷,但是无法对线程进行更详细的设置
#pragma mark -创建第一个线程
-(void)test1{
//实例化一个线程对象,该方法有返回值,返回线程对象
NSThread *thread=[[NSThread alloc] initWithTarget:self selector:@selector(run1) object:nil];
//启动线程,在新开的线程中执行run方法
[thread start];
}
-(void)run1{
for (int i=0; i<4; i++) {
NSLog(@"当前线程:%@,序号:%d",[NSThread currentThread],i);
}
}
#pragma mark -创建第二个线程
-(void)test2{
//创建线程,该方法没有返回值
[NSThread detachNewThreadSelector:@selector(run2:) toTarget:self withObject:@"test2"];
}
-(void)run2:(NSString *)str{
for (int i=0; i<4; i++) {
NSLog(@"当前线程:%@,序号:%d,参数:%@",[NSThread currentThread],i,str);
}
}
#pragma mark -创建第三个线程
-(void)test3{
//创建线程,该方法没有返回值
[self performSelectorInBackground:@selector(run3:) withObject:@"test3"];
}
-(void)run3:(NSString *)str{
for (int i=0; i<4; i++) {
NSLog(@"当前线程:%@,序号:%d,参数:%@",[NSThread currentThread],i,str);
}
}
#pragma mark -创建第四个线程:设置线程的属性
-(void)test4{
NSThread *newThread=[[NSThread alloc] initWithTarget:self selector:@selector(run4:) object:@"test4"];
newThread.name=@"threadA";//线程的名字
newThread.threadPriority=0.1;//线程优先级, 是一个浮点数, 0.0~1.0, 默认值是0.5, 数值越大,调度的优先级高, 优先级必须很多次的时候才能体现出来
[newThread start];
}
-(void)run4:(NSString *)str{
for (int i=0; i<4; i++) {
NSLog(@"%@ -----%d----%@",[NSThread currentThread],i ,str);
}
}
@end
NSThread的常用方法:
//实例化一个线程对象 start方法启动线程
NSThread *thread=[[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
[thread start];
//创建线程后自动启动线程
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
//隐式创建并启动线程
[self performSelectorInBackground:@selector(run) withObject:nil];
//线程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;
// 获得主线程,判断是否为主线程
+ (NSThread *)mainThread;
- (BOOL)isMainThread;
+ (BOOL)isMainThread;
//获得当前线程
NSThread *current = [NSThread currentThread];
//线程的调度优先级(调度优先级的取值范围是0.0 ~ 1.0,默认0.5,值越大,优先级越高,自己开发时,建议一般不要修改优先级)
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
- (double)threadPriority;
- (BOOL)setThreadPriority:(double)p;
- <3>GCD的用法
/**
* 串行队列的同步执行
* 同步执行:不会开启新线程,在原来的线程(主线程)中一个一个的执行
*/
-(void)gcdTest1{
//1.创建一个串行队列 参数1:队列标签 参数2:队列属性 SERIAL连续的意思
dispatch_queue_t queue=dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
//2.执行任务
//一般只要是使用同步执行,串行队列对添加的同步任务,会立马执行,所以先打印”线程“信息,然后打印"完成"信息
for (int i=0; i<4; i++) {
dispatch_sync(queue, ^{
NSLog(@"线程:%@,序号:%d",[NSThread currentThread],i);
});
}
NSLog(@"完成 %@",[NSThread currentThread]);
}
打印结果
======================================
线程:<NSThread: 0x60000339a940>{number = 1, name = main},序号:0
线程:<NSThread: 0x60000339a940>{number = 1, name = main},序号:1
线程:<NSThread: 0x60000339a940>{number = 1, name = main},序号:2
线程:<NSThread: 0x60000339a940>{number = 1, name = main},序号:3
完成 <NSThread: 0x600003855400>{number = 1, name = main}
/**
* 串行队列的异步执行
* 异步执行:会开启新线程,在新线程中执行,因为是串行,在队列中一个一个的取任务,所以你开启更多个新线程也没有意义,就只开启一个新线程
*/
-(void)gcdTest2{
//1.串行队列
dispatch_queue_t queue=dispatch_queue_create("itcast", DISPATCH_QUEUE_SERIAL);
//2.异步执行
//因为是异步执行,又是串行一个一个的去执行,所以会先执行主任务里面的"完成"信息,然后再执行异步任务“线程”信息
for (int i=0; i<4; i++) {
dispatch_async(queue, ^{
NSLog(@"线程:%@,序号:%d",[NSThread currentThread],i);
});
}
NSLog(@"完成 %@",[NSThread currentThread]);
}
打印结果
======================================
完成 <NSThread: 0x600001a06fc0>{number = 1, name = main}
线程:<NSThread: 0x6000013c1780>{number = 3, name = (null)},序号:0
线程:<NSThread: 0x6000013c1780>{number = 3, name = (null)},序号:1
线程:<NSThread: 0x6000013c1780>{number = 3, name = (null)},序号:2
线程:<NSThread: 0x6000013c1780>{number = 3, name = (null)},序号:3
/**
* 并发队列的异步执行
* 异步执行:一定会开辟新线程的,并发的会开启多个线程
*/
-(void)gcdTest3{
//1.创建一个并行队列 参数1:队列标签 参数2:队列属性 CONCURRENT并发的意思
dispatch_queue_t queue=dispatch_queue_create("cz", DISPATCH_QUEUE_CONCURRENT);
//2.执行
//因为是异步执行,又是并发执行,所以打印“线程”信息的顺序结果不确定,不过先打印出主线程的“完成”信息
for (int i=0; i<4; i++) {
dispatch_async(queue, ^{
NSLog(@"线程:%@,序号:%d",[NSThread currentThread],i);
});
}
NSLog(@"完成 %@",[NSThread currentThread]);
}
打印结果
======================================
线程信息顺序不确定
/**
*并发队列的同步执行
*同步执行:不会开启线程,在原线程中执行任务,虽然是并行,但只有一个线程,还是一条一条的按照顺序执行
*/
-(void)gcdTest4{
//1.创建一个并行队列
dispatch_queue_t queue=dispatch_queue_create("zc", DISPATCH_QUEUE_CONCURRENT);
//2、同步就不会开启新的线程,在原来的线程里面一个一个的执行
for (int i=0; i<4; i++) {
dispatch_sync(queue, ^{
NSLog(@"线程:%@,序号:%d",[NSThread currentThread],i);
});
}
NSLog(@"完成 %@",[NSThread currentThread]);
}
打印结果
======================================
线程:<NSThread: 0x60000338e900>{number = 1, name = main},序号:0
线程:<NSThread: 0x60000338e900>{number = 1, name = main},序号:1
线程:<NSThread: 0x60000338e900>{number = 1, name = main},序号:2
线程:<NSThread: 0x60000338e900>{number = 1, name = main},序号:3
完成 <NSThread: 0x60000338e900>{number = 1, name = main}
/**
* 主队列的异步执行
* 主队列:专门负责在主线程上调度任务,不会在子线程上调度任务
* 主队列的特点:不允许开启新线程,主队列的权限大
* 异步执行:会开启新线程,但是在主队列中无法开启新线程
*
*结果:没有问题,等到test5在主线程上执行结束之后,里面的任务才开始放到主线程上执行,"完成"打印出来,才开始执行定义的异步任务任务
*/
-(void)gcdTest5{
//1.获得主队列 -> 程序启动 -> 至少有一个主线程 -> 一开始就创建主队列
dispatch_queue_t queue=dispatch_get_main_queue();
//2.异步执行任务:把任务放到主队列,但是不需要马上执行
for (int i=0; i<4; i++) {
NSLog(@"调度前,序号:%d",i);
dispatch_async(queue, ^{
NSLog(@"线程:%@,序号:%d",[NSThread currentThread],i);
});
NSLog(@"睡会,序号:%d",i);
[NSThread sleepForTimeInterval:1.0];
}
NSLog(@"完成 %@",[NSThread currentThread]);
}
打印结果
======================================
调度前,序号:0
睡会,序号:0
调度前,序号:1
睡会,序号:1
调度前,序号:2
睡会,序号:2
调度前,序号:3
睡会,序号:3
完成 <NSThread: 0x600002a80000>{number = 1, name = main}
线程:<NSThread: 0x600002a80000>{number = 1, name = main},序号:0
线程:<NSThread: 0x600002a80000>{number = 1, name = main},序号:1
线程:<NSThread: 0x600002a80000>{number = 1, name = main},序号:2
线程:<NSThread: 0x600002a80000>{number = 1, name = main},序号:3
/**
* 主队列的同步执行
* 同步执行:是需要马上执行的,不会等待
* 结果:只有一个主队列,所以主队列上的gcdTest6方法一直占据着,但是又因为是同步的,所以一直在等着下面的任务执行,但是任务有等着gcdTest6执行,所以会造成死锁
*/
-(void)gcdTest6{
dispatch_queue_t queue=dispatch_get_main_queue();
for (int i=0; i<10; i++) {
NSLog(@"调度前,序号:%d",i);
dispatch_sync(queue, ^{
NSLog(@"线程:%@,序号:%d",[NSThread currentThread],i);
});
NSLog(@"睡会,序号:%d",i);
[NSThread sleepForTimeInterval:1.0];
}
NSLog(@"完成%@",[NSThread currentThread]);
}
打印结果
======================================
卡死啦
#pragma mark - 线程之间的通信
/**
线程之间的通信
*/
dispatch_async(dispatch_get_global_queue(0, 0), ^{// 异步执行
//耗时操作放在这里
dispatch_async(dispatch_get_main_queue(), ^{//回到主线程
//一般在主线程处理UI
});
});
#pragma mark - gcd的延迟方法
//方法一:NSObject的延迟方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
//方法二:GCD的延迟方法
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
#pragma mark - gcd的调度组
/**
队列组
分别异步执行多个任务,要等到这些任务全部都执行完毕后,才回到主线程
这样的时候一般会用到队列组
*/
//实例化一个调度组
dispatch_group_t group=dispatch_group_create();
//创建一个队列
dispatch_queue_t queue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//把任务添加到队列queue中
dispatch_group_async(group, queue, ^{
NSLog(@"下载小说A %@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"下载小说B %@",[NSThread currentThread]);
});
dispatch_group_async(group, queue, ^{
NSLog(@"下载小说C %@",[NSThread currentThread]);
});
//一:获得调度组里面的所有异步任务完成的通知
dispatch_group_notify(group, queue, ^{
//这里是异步的,无法知道他在哪个线程里执行,可能会开辟一个新的线程,也可能在原来的哪个线程里执行
NSLog(@"下载完成,请观看--%@",[NSThread currentThread]);
});
//二:指定一个queue,所以可以跨队列通信,这样就可以在主线程里进行更新UI等操作
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//在主线程,完成下列操作,所以线程的number=1
NSLog(@"下载完成 -%@",[NSThread currentThread]);
});
#pragma mark - gcd的一次性执行
/**GCD实现代码只执行一次
使用dispatch_once能保证某段代码在程序运行过程中只被执行1次。可以用来设计单例。
*/
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"程序运行过程中我只执行了一次!");
});
-
<4>NSOperation的用法
NSOperation是将GCD进行封装起来使用的
#pragma mark - NSInvocationOperation的使用
-(void)opTest1{
//1、创建操作
NSOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run1:) object:@"opTest1"];
//2、执行任务:这样直接在当前线程中执行,没有意义
[op start];
}
-(void)run1:(id)obj{
NSLog(@"当前线程:%@,参数:%@",[NSThread currentThread],obj);
}
/**
打印结果
==========================================
当前线程:<NSThread: 0x600001a0e940>{number = 1, name = main},参数:opTest1
*/
-(void)opTest2{
//1、创建主队列,mainQueue跟GCD里面的主队列一样
NSOperationQueue *queue = [NSOperationQueue mainQueue];
//2、创建操作
for (int i=0; i<4; i++) {
NSOperation *op=[[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run2:) object:@"opTest2"];
[queue addOperation:op];
}
NSLog(@"完成%@",[NSThread currentThread]);
}
-(void)run2:(id)obj{
NSLog(@"当前线程:%@,参数:%@",[NSThread currentThread],obj);
}
/**
打印结果
==========================================
完成<NSThread: 0x600001159400>{number = 1, name = main}
当前线程:<NSThread: 0x600001159400>{number = 1, name = main},参数:opTest2
当前线程:<NSThread: 0x600001159400>{number = 1, name = main},参数:opTest2
当前线程:<NSThread: 0x600001159400>{number = 1, name = main},参数:opTest2
当前线程:<NSThread: 0x600001159400>{number = 1, name = main},参数:opTest2
*/
-(void)opTest3{
//1、创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2、创建多个任务,并把多个操作放到队列中
for (int i=0; i<4; i++) {
NSOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run3:) object:@"opTest3"];
[queue addOperation:op];
}
NSLog(@"完成%@",[NSThread currentThread]);
}
-(void)run3:(id)obj{
NSLog(@"当前线程:%@,参数:%@",[NSThread currentThread],obj);
}
/**
打印结果
==========================================
完成<NSThread: 0x60000287e900>{number = 1, name = main}
当前线程:<NSThread: 0x6000038cd7c0>{number = 5, name = (null)},参数:opTest3
当前线程:<NSThread: 0x6000038cd780>{number = 3, name = (null)},参数:opTest3
当前线程:<NSThread: 0x6000038c4a80>{number = 4, name = (null)},参数:opTest3
当前线程:<NSThread: 0x6000038cd840>{number = 6, name = (null)},参数:opTest3
*/
#pragma mark - NSBlockOperation的使用
-(void)opTest4{
//1、创建队列
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
//2、创建多个任务,并多个操作放到队列中
for (int i=0; i<4; i++) {
NSOperation *op=[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"当前线程:%@,序号:%d",[NSThread currentThread],i);
}];
[queue addOperation:op];
}
NSLog(@"完成%@",[NSThread currentThread]);
}
/**
打印结果
=======================================================
完成<NSThread: 0x600001eb5400>{number = 1, name = main}
当前线程:<NSThread: 0x6000031bd040>{number = 4, name = (null)},序号:2
当前线程:<NSThread: 0x6000031bcf80>{number = 6, name = (null)},序号:1
当前线程:<NSThread: 0x6000031a48c0>{number = 3, name = (null)},序号:0
当前线程:<NSThread: 0x6000031a6340>{number = 5, name = (null)},序号:3
*/
-(void)opTest5{
//1、创建主队列,mainQueue跟GCD里面的主队列一样
NSOperationQueue *queue=[NSOperationQueue mainQueue];
//2、生成多个操作,并添加到队列中
for (int i=0; i<4; i++) {
NSBlockOperation *op=[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"当前线程:%@,序号:%d",[NSThread currentThread],i);
}];
[queue addOperation:op];
}
NSLog(@"完成%@",[NSThread currentThread]);
}
/**
打印结果
先打印出“完成”信息,然后再打印线程信息
结果:在主队列上执行代码,只有一条线程,所以先执行完毕opTest4之后,再在主线程上执行新建的4个任务,一个一个执行
=======================================================
完成<NSThread: 0x600001f95400>{number = 1, name = main}
当前线程:<NSThread: 0x600001f95400>{number = 1, name = main},序号:0
当前线程:<NSThread: 0x600001f95400>{number = 1, name = main},序号:1
当前线程:<NSThread: 0x600001f95400>{number = 1, name = main},序号:2
当前线程:<NSThread: 0x600001f95400>{number = 1, name = main},序号:3
*/
#pragma mark -NSBlockOperation的更简单的使用
-(void)opTest6{
//1.创建队列
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
NSBlockOperation *op1=[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"当前线程:%@----op1",[NSThread currentThread]);
}];
[queue addOperation:op1];
//NSBlockOperation里面还有一个方法 addExecutionBlock 可以在操作里面再添加操作,其前提是:op1是NSBlockOperation类的对象
[op1 addExecutionBlock:^{
NSLog(@"当前线程:%@----op1可以再添加操作",[NSThread currentThread]);
}];
}
/**
打印结果
==================================================================
当前线程:<NSThread: 0x60000266dd00>{number = 4, name = (null)}----op1可以再添加操作
当前线程:<NSThread: 0x60000266dc40>{number = 3, name = (null)}----op1
*/
-(void)opTest7{
//1.创建队列
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
for (int i=0; i<4; i++) {
//2、向队列添加任务
[queue addOperationWithBlock:^{
NSLog(@"当前线程:%@,序号:%d",[NSThread currentThread],i);
}];
}
}
/**
打印结果
==================================================================
当前线程:<NSThread: 0x600001a42680>{number = 5, name = (null)},序号:3
当前线程:<NSThread: 0x600001a46b00>{number = 4, name = (null)},序号:1
当前线程:<NSThread: 0x600001a48600>{number = 6, name = (null)},序号:2
当前线程:<NSThread: 0x600001a42640>{number = 3, name = (null)},序号:0
*/
#pragma mark - NSBlockOperation线程间的通信
//如果要做线程间的通信,可以使用[NSOperationQueue mainQueue]拿到主队列,往主队列中添加操作(更新UI)
-(void)opTest8{
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSLog(@"耗时操作...%@",[NSThread currentThread]);
//在主线程执行下列操作,比如更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
NSLog(@"更新UI ...%@",[NSThread currentThread]);
}];
}];
}
#pragma mark -NSOperation的最大并发数
-(void)opTest9{
//最大并发数并不是指线程数,而是指同一时间内只能有几个任务能同时进行
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount=2;
for (int i=0; i<100; i++) {
NSOperation *op=[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@--%d",[NSThread currentThread],i);
[NSThread sleepForTimeInterval:1.0];
}];
[queue addOperation:op];
}
}
#pragma mark -NSOperation的挂起、取消全部操作
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
//queue.operationCount : 操作队列里面的操作数量
//queue.suspended : 布尔类型,YES代表暂停,NO表示恢复队列,默认状态是0,也就是NO
//对应暂停、继续按钮
-(void)opTest10{
//判断当前操作队列里面的操作数量。假如没有操作,这条队列里面就不要有暂停和继续的功能,假如有操作,我们就需要有暂停和继续的功能
if(queue.operationCount==0){
NSLog(@"没有操作");
return;
}
//暂停和继续
//suspended是布尔类型的,YES代表暂停,NO表示恢复队列,默认状态是0,也就是NO
queue.suspended=!queue.suspended;
if (queue.suspended) {
NSLog(@"暂停");
}else{
NSLog(@"继续");
}
}
//对应取消操作
-(void)opTest11{
//取消队列的所有操作,取消操作并不会影响队列的挂起状态
//这里需要知道的就是,取消的是队列里面的操作,但是线程中正在执行的是无法取消的
[queue cancelAllOperations];
NSLog(@"取消队列里面的所有操作");
}
#pragma mark -NSOperation的依赖关系
//1.下载一个小说的安装包, 2.解压缩,删除压缩包 3.更新UI
NSOperationQueue *queue=[[NSOperationQueue alloc] init];
-(void)dependecy{
NSBlockOperation *op1=[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1.下载一个小说的压缩包,%@",[NSThread currentThread]);
}];
NSBlockOperation *op2=[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2.解压缩,删除压缩包 %@",[NSThread currentThread]);
}];
NSBlockOperation *op3=[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"3.更新UI %@",[NSThread currentThread]);
}];
//制定依赖关系,op2要等op1执行完毕,op3要等待op2执行完毕,但是这里的op3是在主线程中执行的,所以要等到Come here打印出来,才会打印op3的操作
//依赖关系是可以跨队列的,(像按照顺序的在子线程执行op1,op2,然后在主线程上执行op3,这就是跨队列的)
[op2 addDependency:op1];
[op3 addDependency:op2];
//WaitUntilFinished类似于GCD的调度组的通知,NO就不会等待队列里面的任务执行,直接打印Come here ,
//要是是YES的话就会等待3个任务执行完毕再打印“Come here”,但是因为此处的op3是在主线程中执行,所以会在打印出“Come here”后才会执行op3
//在queue中执行op1、op2
[queue addOperations:@[op1,op2] waitUntilFinished:YES];
//在主线程执行op3,更新UI
[[NSOperationQueue mainQueue] addOperation:op3];
NSLog(@"Come here");
}
4、总结
<1>你理解的多线程?
多线程就是说在一个进程(iOS中可以理解为应用程序)中,除了主线程之外,还可以开辟些其他进程,用于处理一些耗时操作。<2>iOS的多线程方案有哪几种?
四种:pthread、NSThread、GCD、NSOperation。
其他三种都是在pthread的基础上实现的,一般使用GCD就足够了,当有很多高级用法的时候,才使用NSOperation。
具体区别见上面详解。<3> NSOperation 和 GCD 的区别
(1)、GCD将任务通过block的方式添加到队列(队列分为:串行、并发(全局)),指定执行任务的方法(同步(阻塞)、异步)
GCD中的线程通信是拿到主队列dispatch_main_queue,在主队列中更新UI
(2)、NSOPeration将任务添加到队列(并发(全局)、异步)
[NSOpeartionQueue mainQueue]主队列,任务添加到主队列中,就会在主线程执行
GCD中可以做到一次性执行dispatch_once_t、延迟执行、调度组,NSOPeration不好实现
NSOPeration提供了一些GCD不好实现的功能:最大并发数、暂停和继续、取消全部任务、依赖关系