×

iOS中的多线程

96
皆为序幕__so
2017.03.27 14:32* 字数 5407

多线程的一些相关概念

什么是进程

1、在系统中正在运行的一个应用程序。

2、每个进程之间是独立的,每个进程均运行在其专用而且受保护的内存空间内。

什么是线程

一个进程要想执行任务,必须得有一个线程,而且每一个进程中至少有一个线程

一个进程的所有任务都在线程中执行

什么是线程的串行

一个线程中的任务都是串行执行的

如果要在一个线程中执行多个任务,那么只能一个一个按顺序来执行这些任务

同一时间内,一个线程只能执行一个任务

比如在一个线程中下载几个文件(文件A,文件B,文件C)


多线程

什么是多线程

1、一个进程中可以开启多条线程,每条线程可以并行执行不同的任务

2、比如同时开启三条线程分别下载几个文件(文件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----%d--%@",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

其实第一组数字1372表示的是当前程序所处的 进程 ID,而第二组数字69783和69782则表示当前所处的子线程 ID,number也可以作为线程的标识,上述代码没有给线程起名字,因此为null,所以我们就可以通过线程ID进行判断是否成功开启了一个子线程

关于NSThread

NSThread可能是我们在OC开发中接触最早的多线程实现技术,而且NSThread的实现多线程的方式也有三种,下面就通过代码看一下

-(void)createThread{

//创建线程 1:通过alloc init进行创建,需要手动创建

NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"jack"];

thread.name = @"my-thread";

thread.threadPriority = 0.1;

//启动线程

[thread start];

//创建方式 2:通过 detachNewThreadSelector 方式创建并执行线程

[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"rose"];

//创建方式 3:隐式创建后自动启动线程

[self performSelectorInBackground:@selector(run:) withObject:@"wahaha"];

}

-(void)run:(NSString *)str{

 for (NSInteger i = 0; i<200; i++) {

NSLog(@"-buttonClick-%d-%@--%@",i,str,[NSThread currentThread]);

if(i == 199){

//回归主线程

[self performSelectorOnMainThread:@selector(runMainThread) withObject:nil waitUntilDone:YES];

}

}

}

采用第一种方式:设置一些线程属性;例如线程 名字,从控制台信息可以看出来,当设置了不同的NSThread对象的优先级属性,可以控制其执行的顺序,优先级越高,越先执行;而设置名字属性后,可以通过调试监控当前所处线程,便于问题分析

第二、三种,创建和操作简单

线程的状态

当我们新建一个线程对象的时候,系统就会为其分配一块内存,当你调用线程的开始方法时,就相当于把这个线程放在了线程池里面,等待CPU去调用它,当线程池中有多个线程,那么CPU就会在这几个线程之间来回切换,但是当线程调用了sleep或同步锁时,该调用的线程就会被阻塞,当sleep或锁结束时,CPU再次进行切换中,当线程任务执行完,该线程就会释放。

+ (NSThread *)mainThread; // 获得主线程

- (BOOL)isMainThread; // 是否为主线程

+ (BOOL)isMainThread; // 是否为主线程

[NSThread currentThread];// 获取当前线程

- (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在主线程,下载图片载子线程,当图片下载完后,回归主线程并显示。

关于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的队列类型

GCD的并发队列:可以让多个任务并发执行,并发功能只有在异步dispatch_async函数下才有效

GCD的串行队列:让任务一个接着一个地执行

:有四个容易混淆的概念:同步、异步、并发、串行

//同步和异步主要影响能不能开线程

//串行和并行主要影响任务的执行方式

GCD并发队列

异步函数 + 并发队列:会开新线程

//1、第一种:创建一个并发队列

//参数1:队列的名字

//参数2:队列的类型(并发:DISPATCH_QUEUE_CONCURRENT,串行:DISPATCH_QUEUE_SERIAL)

dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_CONCURRENT);

//1、第二种:获得系统自带全局的并发队列

//参数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<10; i++) {

NSLog(@"1--%@",[NSThread currentThread]);

}

});

dispatch_async(queue, ^{

for (NSInteger i = 0; i<10; i++) {

NSLog(@"2--%@",[NSThread currentThread]);

}

});

同步函数 + 并发队列 :不会开新线程

//1、获得全局的并发队列

//参数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<10; i++) {

NSLog(@"1--%@",[NSThread currentThread]);

}

});

dispatch_sync(queue, ^{

for (NSInteger i = 0; i<10; i++) {

NSLog(@"2--%@",[NSThread currentThread]);

}

});

GCD串行队列

异步函数 + 串行队列 :会开新线程,但是任务是串行的,执行完一个再执行下一个

//1、第一种:创建一个串行队列(没有全局,只能创建)

//参数1:队列的名字

//参数2:队列的类型(并发:DISPATCH_QUEUE_CONCURRENT,串行:DISPATCH_QUEUE_SERIAL)

dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_SERIAL);

//1、第二种:创建一个串行队列

//参数1:队列的名字

//参数2:队列的类型(NULL)

dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", NULL);

//2、将任务加入队列

dispatch_async(queue, ^{

for (NSInteger i = 0; i<10; i++) {

NSLog(@"1--%@",[NSThread currentThread]);

}

});

dispatch_async(queue, ^{

for (NSInteger i = 0; i<10; i++) {

NSLog(@"2--%@",[NSThread currentThread]);

}

});

同步函数 + 串行队列 :不会开新线程,在当前线程执行任务

//1、第一种:创建一个串行队列(没有全局,只能创建)

//参数1:队列的名字

//参数2:队列的类型(并发:DISPATCH_QUEUE_CONCURRENT,串行:DISPATCH_QUEUE_SERIAL)

dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", DISPATCH_QUEUE_SERIAL);

//1、第二种:创建一个串行队列

//参数1:队列的名字

//参数2:队列的类型(NULL)

dispatch_queue_t queue = dispatch_queue_create("com.xiaoyi.queue", NULL);

//2、将任务加入队列

dispatch_sync(queue, ^{

for (NSInteger i = 0; i<10; i++) {

NSLog(@"1--%@",[NSThread currentThread]);

}

});

dispatch_sync(queue, ^{

for (NSInteger i = 0; i<10; 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<10; i++) {

NSLog(@"1--%@",[NSThread currentThread]);

}

});

dispatch_async(queue, ^{

for (NSInteger i = 0; i<10; i++) {

NSLog(@"2--%@",[NSThread currentThread]);

}

});

同步函数 + 主队列 :会卡死,线程间相互制约

//获取主队列

dispatch_queue_t queue = dispatch_get_main_queue();

//2、将任务加入队列

dispatch_sync(queue, ^{

for (NSInteger i = 0; i<10; i++) {

NSLog(@"1--%@",[NSThread currentThread]);

}

});

dispatch_sync(queue, ^{

for (NSInteger i = 0; i<10; 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;

});

});

GCD其他相关函数

barrier函数

dispatch_barrier_sync(queue, ^{

NSLog(@"--barrier--%@",[NSThread currentThread]);

});

在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]);

}

});

输出log:

延时函数

//第一种延时

[self performSelector:@selector(run) withObject:nil afterDelay:2];

//第二种延时(取决于dispatch_get_main_queue队列)

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:队列 参数3:数量

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_apply(10, queue, ^(size_t index) {

NSLog(@"%zu",index);

});

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;

});

});

当image1和image2全部下载完之后才响应dispatch_group_notify这个函数

关于NSOperation

NSOperation的作用

配合使用NSOperation和NSOperationQueue也能实现多线程编程

NSOperation的作用

先将需要执行的操作封装到一个NSOperation对象中

然后将NSOperation对象添加到NSOperationQueue中

系统会自动将NSOperationQueue中的NSOperation取出来

将取出的NSOperation封装的操作放到一条新线程中执行

NSOperation的子类

NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类

使用NSOperation的子类的方式有3种

NSInvocationOperation

NSBlockOperation

自定义子类继承NSOperation,实现内部相应的方法

关于NSInvocationOperation

创建NSInvocationOperation对象

//初始化Operation子类

NSInvocationOperation *operation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(run) object:nil];

//开启

[operation start];

调用start方法开始执行操作,一旦执行操作就会调用run方法

注:默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作

只有NSOperation放到一个NSOperationQueue中,才会异步执行

关于NSBlockOperation

创建NSBlockOperation对象

//初始化Operation子类

NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{

//在主线程

NSLog(@"--%@",[NSThread currentThread]);

}];

//添加额外的任务(在子线程执行)

[operation addExecutionBlock:^{

NSLog(@"--%@",[NSThread currentThread]);

}];

注:只要NSBlockOperation封装的操作数大于1,就会异步执行

关于NSOperationQueue

NSOperationQueue的作用

NSOperation可以调用start方法来执行任务,默认是同步的

如果将NSOperation添加到NSOperationQueue(操作队列)中,系统会自动异步执行NSOperation中的操作

//创建队列

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//创建操作(任务)

//创建--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(@"-3--%@",[NSThread currentThread]);

}];

NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"-4--%@",[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(@"-6--%@",[NSThread currentThread]);

}];

通过输出的log可以看出确实有开启线程,所有只要将操作添加的队列里,就可以实现多线程操作

NSOperationQueue设置最大并发操作数

//创建队列

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

//设置最大并发操作数(不管加入队列有多少操作,实际队列并发数为3)

queue.maxConcurrentOperationCount = 2;

[queue addOperationWithBlock:^{

NSLog(@"-1--%@",[NSThread currentThread]);

}];

[queue addOperationWithBlock:^{

NSLog(@"-2--%@",[NSThread currentThread]);

}];

[queue addOperationWithBlock:^{

NSLog(@"-3--%@",[NSThread currentThread]);

}];

[queue addOperationWithBlock:^{

NSLog(@"-4--%@",[NSThread currentThread]);

}];

尽管队列里面有4个并发操作,但是每次仅有俩个并发操作

NSOperationQueue设置队列挂起与取消

-(void)createOperationQueueSuspended{

//创建队列

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

queue.maxConcurrentOperationCount = 1;

[queue addOperationWithBlock:^{

NSLog(@"-1--%@",[NSThread currentThread]);

[NSThread sleepForTimeInterval:1.0];

}];

[queue addOperationWithBlock:^{

NSLog(@"-2--%@",[NSThread currentThread]);

[NSThread sleepForTimeInterval:1.0];

}];

[queue addOperationWithBlock:^{

NSLog(@"-3--%@",[NSThread currentThread]);

[NSThread sleepForTimeInterval:1.0];

}];

[queue addOperationWithBlock:^{

NSLog(@"-4--%@",[NSThread currentThread]);

[NSThread sleepForTimeInterval:1.0];

}];

//设置队列挂起或者取消的话都必须是在block方法执行完之后才有效

[queue addOperationWithBlock:^{

for(NSInteger i = 0;i<20;i++){

NSLog(@"-5--%d---%@",i,[NSThread currentThread]);

}

}];

self.queue = queue;

}

-(void)operationSetSuspended{

if(self.queue.suspended){

//恢复队列,继续执行

self.queue.suspended  = NO;

}else{

//挂起(暂停队列)

self.queue.suspended  = YES;

}

}

-(void)operationSetCancel{

[self.queue cancelAllOperations];

}

当队列调用了队列挂起的方法( self.queue.suspended = YES;),队列里的执行方法立即停止,但是有一点需要注意的是,当block操作中,队列挂起是不起作用的,它是无法停止的,必须操作执行结束后才会生效。

当队列调用取消( [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]);

}];

//设置依赖(op1和op2执行完之后才执行3)

[op3 addDependency:op1];

[op3 addDependency:op2];

[queue addOperation:op1];

[queue addOperation:op2];

[queue addOperation:op3];

//监听一个操作的执行完成

[op3 setCompletionBlock:^{

NSLog(@"执行完成");

}];

注:一定要避免相互依赖,比如

[op3 addDependency:op1];

[op1 addDependency:op3];    //错误的写法---相互依赖

NSOperationQueue队列间的数据通信

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];

先创建了一个普通队列,在普通队列里执行俩个操作,当子线程的图片都下载下来后,回归主线程将其显示在UI界面上。


最后,附上以上的Log版demo,Git:github.com/hejiasu/Multithreading

CSDN:blog.csdn.net/hejiasu/article/details/60875116

iOS开发
Web note ad 1