多线程的一些相关概念
什么是进程
- 在系统中正在运行的一个应用程序。
- 每个进程之间是独立的,每个进程均运行在其专用而且受保护的内存空间内。
什么是线程
- 一个进程要想执行任务,必须得有一个线程,而且每一个进程中至少有一个线程
- 进程的所有任务都在线程中执行
什么是线程的串行
- 一个线程中的任务都是串行执行的
- 如果要在一个线程中执行多个任务,那么只能一个一个按顺序来执行这些任务
- 同一时间内,一个线程只能执行一个任务
比如在一个线程中下载几个文件(文件A,文件B,文件C)
多线程
什么是多线程
- 一个进程中可以开启多条线程,每条线程可以并行执行不同的任务
比如同时开启三条线程分别下载几个文件(文件A,文件B,文件C)
多线程原理
1、同一时间,CPU只能处理一条线程,只有一条线程在工作
2、多线程并发,其实是CPU快速的在多条线程之间切换
3、如果多线程切换速度特别快,就造成了多线程并发执行的假象
**注:**如果线程非常多,CPU会在N多线程之间切换,CPU消耗就会特别大,每条线程被调用额频率就会被降低,所有要适当使用多线程
多线程优缺点
优点
1、能适当的提高程序执行效率
2、能够适当的提高资源利用率(CPU,内存利用率)
缺点
1、创建线程是有开销的,iOS下主要成本包括:内核数据结构(大约1KB),栈空间(子线程512KB,主线程1MB,也可以使用-setStackSize:设置,但是必须是4K的倍数,而且最小是16K),创建线程大约需要90毫秒的创建时间
2、若开启大量线程,会降低线程上的性能,CPU消耗越大
3、多线程使用太多,会使程序设计更加复杂,比如线程间的通信,数据共享等等
多线程在iOS开发中的应用
什么是主线程
- 一个iOS程序运行后,默认会开启的一条线程,称为:主线程或UI线程
主线程作用
- 显示\刷新界面
- 处理UI事件,比如点击事件、滚动事件、拖拽事件等等
主线程使用注意
- 不要把耗时的操作放在主线程中
- 耗时的操作会卡住主线程,严重影响流畅度
耗时操作的执行如果把耗时操作放在主线程里,当用户在第5秒时点击按钮时,因为线程里的操作必须是串行的,此时的这个点击事件会排在10秒耗时操作之后,直到10秒的耗时操作结束后,才能执行按钮点击事件,这样就会造成UI卡住的现象,如下图:
如果把耗时操作放在子线程里,此刻,主线程和子线程同事进行,当用户在第5秒时点击按钮时,子线程做耗时操作,主线程响应界面操作,所以这样就不会造成UI卡住的现象,所有要把耗时操作放在子线程后台线程中,如图:
iOS中多线程的实现方案
关于Pthread
- (IBAction)buttonClick:(id)sender {
//PThread的创建
pthread_t thread;
pthread_create(&thread, NULL, run, NULL);
//PThread的创建
pthread_t thread1;
pthread_create(&thread1, NULL, run, NULL);
}
void * run(void *param){
NSLog(@"当前线程--%@",[NSThread currentThread]);
for (NSInteger i = 0; i<5000; i++) {
NSLog(@"-buttonClick-%ld-%@",(long)i,[NSThread currentThread]);
}
return NULL;
}
从代码中可以看出Pthread的创建执行其实也是比较简单的,不过实现过程是通过C语言进行的,从创建方法pthread_create(<#pthread_t _Nullable restrict _Nonnull#>, <#const pthread_attr_t restrict _Nullable#>, <#void _Nullable ( _Nonnull)(void * _Nullable)#>, <#void *restrict _Nullable#>)可以看出,第一个参数是需要一个Pthread 对象指针,第三个是需要一个C语言函数方法(就当于OC中绑定的执行方法),至于第二个和第四个参数,暂时没有什么用,可以直接传入NULL
数字1811表示的是当前程序所处的进程 ID
数字27155和27156则表示当前所处的子线程 ID
number也可以作为线程的标识
上述代码没有给线程起名字,因此为null
所以我们就可以通过线程ID进行判断是否成功开启了一个子线程
关于NSThread
- 通过alloc init进行创建
//创建线程
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"jack"];
thread.name = @"my-thread";
thread.threadPriority = 0.1;
//启动线程
[thread start];
- 通过 detachNewThreadSelector 方式创建并执行线程
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"rose"];
- 隐式创建后自动启动线程
[self performSelectorInBackground:@selector(run:) withObject:@"wahaha"];
采用第一种方式:设置一些线程属性;例如线程 名字,从控制台信息可以看出来,当设置了不同的NSThread对象的优先级属性,可以控制其执行的顺序,优先级越高,越先执行;而设置名字属性后,可以通过调试监控当前所处线程,便于问题分析
第二、三种,创建和操作简单
-
线程的状态
当我们新建一个线程对象的时候,系统就会为其分配一块内存,当你调用线程的开始方法时,就相当于把这个线程放在了线程池里面,等待CPU去调用它,当线程池中有多个线程,那么CPU就会在这几个线程之间来回切换,但是当线程调用了sleep或同步锁时,该调用的线程就会被阻塞,当sleep或锁结束时,CPU再次进行切换中,当线程任务执行完,该线程就会释放。
@property (class, readonly, strong) NSThread *currentThread// 获取当前线程
+ (NSThread *)mainThread; // 获得主线程
- (BOOL)isMainThread; // 是否为主线程
+ (BOOL)isMainThread; // 是否为主线程
- (void)start; // 进入就绪状态 -> 运行状态。当线程任务执行完毕,自动进入死亡状态
+ (void)sleepUntilDate:(NSDate *)date;// 进入阻塞状态
+ (void)sleepForTimeInterval:(NSTimeInterval)time;// 进入阻塞状态
+ (void)exit;//强制停止线程,一旦线程停止(死亡)了,就不能再次开启任务
多线程的竞争(线程锁)
一块资源可能被多个线程共享,也就是多个线程可能会访问同一个资源、同一个对象、同一个变量,就会出现线程安全问题
当线程A去访问文件时,需要将文件锁住,读取并运算结束后,将其解锁,线程B再去访问,线程B访问时需要再次将文件加锁并运算,结束后再解锁。这样就避免了俩个线程同时访问文件而造成文件错误。
互斥锁使用
- 格式:
@synchronized(锁对象) { // 需要锁定的代码 }
- **互斥锁的使用前提:**多条线程抢夺同一块资源
- **注意:**锁定1份代码只用1把锁,用多把锁是无效的
- 互斥锁的优缺点
- 优点:能有效防止因多线程抢夺资源造成的数据安全问题
- 缺点:需要消耗大量的CPU资源
- 线程同步
- 线程同步的意思是:多条线程在同一条线上执行(按顺序地执行任务)
- 互斥锁,就是使用了线程同步技术,保证线程的串行
- 线程异步
- 多线程默认的就是异步,这个不需要多做解释
- 原子和非原子属性
- atomic:原子属性,默认为setter方法加锁(默认就是atomic),线程安全,需要消耗大量的资源
- nonatomic:非原子属性,不会为setter方法加锁,适合内存小的移动设备
- 注:iOS开发中,所有属性都声明为nonatomic,尽量避免多线程抢夺同一资源。尽量将加锁,资源抢夺业务交给服务器
线程间通信
- 线程间通信
在一个进程中,线程往往不是孤立存在,多个线程需要经常进行通信,一般表现为一个线程传递数据给另外一个线程,或者在一个线程中执行完一个特定任务后,转到另一个线程继续执行任务 - 线程间通信常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait; - (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
- 示例
处理UI在主线程,下载图片载子线程,当图片下载完后,回归主线程并显示。
- (IBAction)showImageView:(UIButton *)sender {
NSLog(@"%@",[NSThread currentThread]);
[self performSelectorInBackground:@selector(download) withObject:nil];
}
-(void)download{
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image =[UIImage imageWithData:data];
NSLog(@"%@",[NSThread currentThread]);
[self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
}
-(void)showImage:(UIImage *)image{
self.imageView.image = image;
NSLog(@"%@",[NSThread currentThread]);
}
关于GCD
- 什么是GCD
全称Grand Central Dispatch,纯C语言,提供了非常大的函数 - GCD优势
CGD是苹果公司为多核的并行运算提供的方案(iOS4开始)
GCD会自动利用更多的CPU内核
GCD会自动管理线程生命周期(创建线程、调度线程、销毁线程)程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理的代码 - GCD俩个核心概念
任务:执行什么操作
队列:用来存放任务 - GCD使用
定制任务(想做的操作)
将任务添加到队列当中(GCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则,先进先出,后进后出。) - GCD执行
//同步方式执行 dispatch_sync(dispatch_queue_t _Nonnull queue, <^(void)block>) //异步的方式执行 dispatch_async(dispatch_queue_t _Nonnull queue, <^(void)block>) queue:队列 block:任务
- 同步和异步的区别:
- 同步:只能在当前线程中执行任务,不具备开启新线程的能力
- 异步:可以在新的线程中执行任务,具备开启新线程的能力
- GCD的队列类型
- 并发队列:可以让多个任务并发执行,并发功能只有在异步dispatch_async函数下才有效
- 串行队列:让任务一个接着一个地执行
注:有四个容易混淆的概念:同步、异步、并发、串行
同步和异步主要影响能不能开线程
串行和并行主要影响任务的执行方式
GCD并发队列
- 异步函数 + 并发队列:会开新线程
//1、创建一个并发队列
//第一种:自定义(队列的名字,队列的类型)
//队列的类型(并发:DISPATCH_QUEUE_CONCURRENT,串行:DISPATCH_QUEUE_SERIAL)
// dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_CONCURRENT);
//第二种:获得全局的并发队列(参数1:优先级(官方建议用default),参数2:目前没有意义,官方文档提示传0)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//2、将任务加入队列
dispatch_async(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"1--%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"2--%@",[NSThread currentThread]);
}
});
- 同步函数 + 并发队列 :不会开新线程
//1、创建一个并发队列
//第一种:自定义(队列的名字,队列的类型)
//队列的类型(并发:DISPATCH_QUEUE_CONCURRENT,串行:DISPATCH_QUEUE_SERIAL)
// dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_CONCURRENT);
//第二种:获得全局的并发队列(参数1:优先级(官方建议用default),参数2:目前没有意义,官方文档提示传0)
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//2、将任务加入队列
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"1--%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"2--%@",[NSThread currentThread]);
}
});
GCD串行队列
- 异步函数 + 串行队列 :会开新线程,但是任务是串行的,执行完一个再执行下一个
//1、创建一个串行队列(没有全局,只能创建)
//第一种:队列的名字,队列的类型
//队列的类型(并发:DISPATCH_QUEUE_CONCURRENT,串行:DISPATCH_QUEUE_SERIAL)
dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_SERIAL);
//第二种:获得全局的并发队列(参数1:队列的名字,参数2:队列的类型(NULL)
// dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", NULL);
//2、将任务加入队列
dispatch_async(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"1--%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"2--%@",[NSThread currentThread]);
}
});
- 同步函数 + 串行队列 :不会开新线程,在当前线程执行任务
//1、创建一个串行队列(没有全局,只能创建)
//第一种:队列的名字,队列的类型
//队列的类型(并发:DISPATCH_QUEUE_CONCURRENT,串行:DISPATCH_QUEUE_SERIAL)
dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_SERIAL);
//第二种:获得全局的并发队列(参数1:队列的名字,参数2:队列的类型(NULL)
// dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", NULL);]
//2、将任务加入队列
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"1--%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"2--%@",[NSThread currentThread]);
}
});
GCD主队列(特殊的串行队列)
-
放在主队列中的任务,都会放在主线程中执行
-
使用dispatch_get_main_queue()获得主队列
-
异步函数 + 主队列 :只在主线程中执行任务
//获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
//2、将任务加入队列
dispatch_async(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"1--%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"2--%@",[NSThread currentThread]);
}
});
- 同步函数 + 主队列 :会卡死,线程间相互制约
//获取主队列
dispatch_queue_t queue = dispatch_get_main_queue();
//2、将任务加入队列
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"1--%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (NSInteger i = 0; i<5; i++) {
NSLog(@"2--%@",[NSThread currentThread]);
}
});
-
各种队列的执行效果
注:
1、使用同步函数往当前串行队列中添加任务,会卡住当前的串行队列
2、使用异步函数是需要把当前方法执行,再去执行异步函数 -
GCD线程间通信
//图片子线程下载
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image =[UIImage imageWithData:data];
//图片主线程显示
dispatch_sync(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
- barrier函数
在barrier函数前面执行的任务执行结束后它后面的任务才会执行
这个queue不能是全局的队列,最好自己创建,例如:
dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i<3; i++) {
NSLog(@"1----%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<3; i++) {
NSLog(@"2----%@",[NSThread currentThread]);
}
});
dispatch_barrier_sync(queue, ^{
NSLog(@"--barrier--%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<3; i++) {
NSLog(@"3----%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i<3; i++) {
NSLog(@"4----%@",[NSThread currentThread]);
}
});
- 延时函数
//第一种延时
[self performSelector:@selector(run) withObject:nil afterDelay:2];
//第二种延时
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"run");
});
//第三种延时
[NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(run) userInfo:nil repeats:NO];
NSLog(@"start");
- 一次性代码
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"--程序在整个加载过程只会加载一次");
});
- 快速迭代(主要用于并发队列)
//参数1: 指定重复次数
//参数2:对象的DispatchQueue
//参数3:带有参数的Block, index的作用是为了按执行的顺序区分各个Block
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%@--%zu",[NSThread currentThread],index);
});
NSLog(@"done");
GCD队列组
- 队列组就是可以对多个队列进行操作的一个组,在队列组中可以对不同队列进行操作监听结果等等
//创建一个队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//创建一个队列组
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
NSLog(@"download image1 start");
//下载图片1
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
self.image1 =[UIImage imageWithData:data];
NSLog(@"download image1 end");
});
dispatch_group_async(group, queue, ^{
NSLog(@"download image2 start");
//下载图片2
NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
self.image2 =[UIImage imageWithData:data];
NSLog(@"download image2 end");
});
//当这个队列组的所有队列全部完成,就会收到这个消息
dispatch_group_notify(group, queue, ^{
NSLog(@"download all images");
//合成新图片
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
[self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
[self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
//在主线程显示
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
});
});
关于NSOperation
- 配合使用NSOperation和NSOperationQueue也能实现多线程编程
- 先将需要执行的操作封装到一个NSOperation对象中
- 然后将NSOperation对象添加到NSOperationQueue中
- 系统会自动将NSOperationQueue中的NSOperation取出来
- 将取出的NSOperation封装的操作放到一条新线程中执行
- NSOperation的子类(抽象类,并不具备封装操作的能力,必须使用它的子类)
- NSInvocationOperation
- NSBlockOperation
- 自定义子类继承NSOperation,实现内部相应的方法
- 关于NSInvocationOperation
调用start方法开始执行操作,一旦执行操作就会调用run方法
注:默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
只有NSOperation放到一个NSOperationQueue中,才会异步执行
- (IBAction)invocationOperation:(id)sender {
//初始化Operation子类
NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];
//开启
[operation start];
}
-(void)run{
NSLog(@"0--%@",[NSThread currentThread]);
}
- 关于NSBlockOperation
注:只要NSBlockOperation封装的操作数大于1,就会异步执行
//初始化Operation子类
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1--%@",[NSThread currentThread]);
}];
//添加额外的任务(在子线程执行)
[operation addExecutionBlock:^{
NSLog(@"2--%@",[NSThread currentThread]);
}];
[operation addExecutionBlock:^{
NSLog(@"3--%@",[NSThread currentThread]);
}];
[operation start];
- 关于NSOperationQueue
- NSOperationQueue的作用
- NSOperation可以调用start方法来执行任务,默认是同步的
- 如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作
- NSOperationQueue的作用
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//NSOperationQueue *queue = [NSOperationQueue mainQueue];
//创建操作(任务)
//创建--NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run1) object:nil];
NSInvocationOperation *op2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run2) object:nil];
//创建--NSBlockOperation
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"-1--%@",[NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"-2--%@",[NSThread currentThread]);
}];
//自定义(需要继承NSOperation,执行的操作需要放在这个自定义类的main中)
SSOperation *op5 = [[SSOperation alloc]init];
//添加任务队列中
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
//也可以直接创建任务到队列中去
[queue addOperationWithBlock:^{
NSLog(@"-3--%@",[NSThread currentThread]);
}];
通过输出的log可以看出确实有开启线程,所有只要将操作添加的队列里,就可以实现多线程操作
- NSOperationQueue设置最大并发操作数
//创建队列
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//设置最大并发操作数(不管加入队列有多少操作,实际队列并发数为3)
queue.maxConcurrentOperationCount = 3;
// //设置为1就成了串行队列
// queue.maxConcurrentOperationCount = 1;
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"-1--%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"-2--%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"-3--%@",[NSThread currentThread]);
}];
[queue addOperationWithBlock:^{
[NSThread sleepForTimeInterval:2]; // 模拟耗时操作
NSLog(@"-4--%@",[NSThread currentThread]);
}];
- NSOperationQueue设置队列挂起与取消
- 当队列调用了队列挂起的方法( self.queue.suspended = YES;),队列里的执行方法立即停止,但是有一点需要注意的是,当block操作中,队列挂起是不起作用的,它是无法停止的,必须操作执行结束后才会生效。
- 当队列调用取消( [self.queue cancelAllOperations])就意味着后续队列不再执行,再次启动需要重新加入队列
#pragma mark-OperationQueue相关设置--设置队列挂起(暂停)
- (IBAction)createOperationQueueSuspended:(id)sender {
//创建队列
self.queue = [[NSOperationQueue alloc]init];
self.queue.maxConcurrentOperationCount = 1;
[self.queue addOperationWithBlock:^{
NSLog(@"-1--%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
[self.queue addOperationWithBlock:^{
NSLog(@"-2--%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
[self.queue addOperationWithBlock:^{
NSLog(@"-3--%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
[self.queue addOperationWithBlock:^{
NSLog(@"-4--%@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//设置队列挂起或者取消的话都必须是在block方法执行完之后才有效
[self.queue addOperationWithBlock:^{
for(NSInteger i = 0;i<5;i++){
NSLog(@"-5--%zd---%@",(long)i,[NSThread currentThread]);
}
}];
}
#pragma mark-设置队列挂起
- (IBAction)operationSetSuspended:(id)sender {
if(self.queue.suspended){
//恢复队列,继续执行
self.queue.suspended = NO;
}else{
//挂起(暂停队列)
self.queue.suspended = YES;
}
}
#pragma mark-设置队列取消(取消就意味着后续队列不再执行,再次启动需要重新加入队列)
- (IBAction)operationSetCancel:(id)sender {
[self.queue cancelAllOperations];
}
- NSOperationQueue设置队列监听与依赖
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"down1---%@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"down2---%@",[NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"down3---%@",[NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"down4---%@",[NSThread currentThread]);
}];
//设置依赖(op1和op3执行完之后才执行2)
[op3 addDependency:op1];
[op3 addDependency:op4];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
//监听一个操作的执行完成
[op3 setCompletionBlock:^{
NSLog(@"执行完成");
}];
注:一定要避免相互依赖,比如
[op3 addDependency:op1];
[op1 addDependency:op3]; //错误的写法---相互依赖
- NSOperationQueue队列间的数据通信
先创建了一个普通队列,在普通队列里执行俩个操作,当子线程的图片都下载下来后,回归主线程将其显示在UI界面上。
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
__block UIImage *image1;
NSBlockOperation *downloadw1 = [NSBlockOperation blockOperationWithBlock:^{
//下载图片1
NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
image1 =[UIImage imageWithData:data];
}];
__block UIImage *image2;
NSBlockOperation *downloadw2 = [NSBlockOperation blockOperationWithBlock:^{
//下载图片2
NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
image2 =[UIImage imageWithData:data];
}];
NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
//合成新图片
UIGraphicsBeginImageContext(CGSizeMake(100, 100));
[image1 drawInRect:CGRectMake(0, 0, 50, 100)];
[image2 drawInRect:CGRectMake(50, 0, 50, 100)];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[[NSOperationQueue mainQueue]addOperationWithBlock:^{
self.imageView.image = image;
}];
}];
[combine addDependency:downloadw1];
[combine addDependency:downloadw2];
[queue addOperation:downloadw1];
[queue addOperation:downloadw2];
[queue addOperation:combine];
最后,附上以上的Log版demo,Git:github.com/hejiasu/Multithreading