iOS -- 浅谈多线程NSThread, GCD, NSOperation

437 阅读10分钟

NSThread

NSThread 是苹果官方提供的面向对象操作线程技术,是Foundation框架提供的最基础的多线程类,每一个NSThread类的对象即代表一个线程,简单方便,可以直接操作线程对象,不过需要开发者自己控制线程的生命周期,在平时较少使用。

NSThread的创建

  1. 通过init初始化方式创建,(实例化方式)
NSString *threadName = @"NSThread";
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadAction:) object:threadName];
[thread start];

使用target对象的selector作为线程的任务执行部分.
NSThread *thread = [[NSThread alloc] initWithBlock:^{
        NSLog(@"NSThread...");
    }];
[thread start];

使用block作为线程的任务执行部分

使用实例化方式创建的NSThread,实例方法会返回NSThread对象,当需要启动线程时需要手动触发start方法,(这里说的启动线程start方法,仅仅是将线程的状态从新建转为就绪,何时执行该线程的任务需要系统自行调度。)

2.通过detachNewThread方式创建,(类方法创建)

[NSThread detachNewThreadSelector:@selector(threadAction:) toTarget:self withObject:nil];

使用target对象的selector作为线程的任务执行部分。
[NSThread detachNewThreadWithBlock:^{
        NSLog(@"thread");
    }];
    
使用block作为线程的任务执行部分

类方法创建的NSThread,返回值为void,创建线程后,会立即启动该线程。

3.调用NSObjectperformSelector:相关方法

在当前线程执行任务
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;

在指定的线程执行任务
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

在主线程执行任务
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;

此方法可以用于线程之间的通信。

NSThread的属性方法

@property (class, readonly, strong) NSThread *currentThread;

类属性,用于获取当前线程,什么线程调用,就返回什么线程
如果是在主线程调用则返回主线程对象
@property (class, readonly, strong) NSThread *mainThread;

类属性,用于返回主线程,不论在什么线程调用都返回主线程
@property double threadPriority;

设置线程的优先级,范围为0-1的doule类型,数字越大优先级越高

我们知道,程序在进行线程调度时,优先级越高的线程被选中到执行状态的可能性越大 但是我们不能仅仅依靠优先级来判断多线程的执行顺序,线程任务的执行还与任务的资源有关系,所以多线程的执行顺序是没有办法预测。

@property (readonly, getter=isExecuting) BOOL executing;

判断线程是否正在执行
@property (readonly, getter=isFinished) BOOL finished;

判断线程是否结束
@property (readonly, getter=isCancelled) BOOL cancelled;

判断线程是否被取消
+ (BOOL)isMultiThreaded;

判断是否是多线程
@property (readonly) BOOL isMainThread

判断当前线程是否是主线程
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

让线程休眠,立即让出当前时间片,让出CPU资源,进入阻塞状态
无论是主线程还是其他线程,只要执行该方法,线程就会休眠
- (void)start;

启动当前新建线程,将线程状态转为就绪
+ (void)exit;

退出当前线程,什么线程执行,什么线程就退出
- (void)cancel;

取消线程
调用该方法会设置cancelled属性为YES,但并不退出线程

Grand Central Dispatch (GCD)

GCD是苹果公司为多核的并行运算提出的解决方案,异步执行任务的技术之一。GCD 会自动利用更多的CPU内核,GCD会自动创建线程、调度任务、销毁线程 即会自动管理线程的生命周期。开发者只需要定义想要执行的任务,并告诉GCD想要执行的任务,不需要编写任何线程管理代码,GCD就能生成必要的线程并计划执行任务。

在了解GCD之前,先来了解几个概念:

  • 任务:就是将要在线程中执行的代码,将这段代码用block封装好,然后将这个任务添加到指定的执行方式(同步执行和异步执行),等待CPU从队列(串行队列和并发队列)中取出任务放到对应的线程中执行。
  • 队列:存放线程任务的队形结构。(系统以FIFO的方式调度队列中的任务进行执行),在GCD中有两种队列:串行队列和并发队列。
  • 同步执行:在某个线程中,任务会一个接着一个执行,前一个没有执行完,后面不能执行。
  • 异步执行:程序会开启多个新线程,任务同一时间可以一起执行。
  • 串行队列:线程任务依照队列中的顺序依次执行。
  • 并发队列:线程任务可以同时一起执行。

同步执行与异步执行

同步执行任务

 dispatch_sync(dispatch_get_global_queue(0, 0), ^{
     NSLog(@"同步执行的任务");
 });
    
 在当前线程执行block任务,并不会开启新的线程,而且必须等待当前代码语句执行完毕,才会执行下一条代码语句。

异步执行任务

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"异步执行的任务");
 });
 
会开启线程执行block任务,但是不一定开启新线程。不用等待当前语句执行完毕,就可以执行下一条语句。

串行队列与并发队列

队列

我们知道,多线程中的队列指的是存放线程任务的队形结构,队列是一种特殊的线性表,遵循先进先出FIFO原则,当有新的任务时,会被追加到队尾,线程在执行任务的时候会从队列的头部按顺序执行任务。

image.png 队列的创建

使用dispatch_queue_create来创建队列对象,传入两个参数,第一个参数表示队列的唯一标识符,可为空。第二个参数用来表示串行队列(DISPATCH_QUEUE_SERIAL)或并发队列(DISPATCH_QUEUE_CONCURRENT)。

串行队列

线程任务依照队列中的顺序依次执行,即每次只执行一个任务,等上一个任务执行完才会开始执行下一个任务。

image.png

dispatch_queue_t queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);
或者
dispatch_queue_t queue = dispatch_queue_create("serial_queue", NULL);

并发队列

一次可以并行多个任务执行,即开启多个线程同时执行不同的任务。不需要等待上一个任务是否执行完成。

image.png

dispatch_queue_t queue1 = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);

GCD中除了这两种队列外,还有两种特殊的队列:主队列和全局并发队列。

主队列 主队列是GCD中提供的特殊的串行队列,主要负责在主线程上调度任务,不会开启新的线程,依赖于主线程,如果在主线程上已经有任务正在执行,主队列会等到主线程空闲后再调度任务。通常是返回主线程更新UI的时候使用。

dispatch_queue_t mainQueue = dispatch_get_main_queue();

全局并发队列

全局并发队列是就是一个并发队列,也是GCD提供的默认的并发队列。

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

第一个参数表示队列的优先级
优先级参数如下:
#define DISPATCH_QUEUE_PRIORITY_HIGH 2 // 高优先级
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0 // 默认(中)优先级
#define DISPATCH_QUEUE_PRIORITY_LOW (-2) // 低优先级
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN // 后台优先级

一般情况下,主队列与全局并发队列的配合使用:

dispatch_async(dispatch_get_global_queue(0, 0), ^{
      // 执行耗时操作
      dispatch_async(dispatch_get_main_queue(), ^{
          // 回到主线程进行UI操作
      });
  });

总的来说,GCD有两种创建任务的方式:同步执行和异步执行,因为全局并发队列就是并发队列,而主队列依赖于主线程,所以可以看成GCD有三种队列:串行队列,并发队列,主队列,这样一来一共有六种不同的执行任务的方式;

执行+队列

1. 同步执行,串行队列

   dispatch_queue_t queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);
   for (int i = 0; i<2; i++) {
       dispatch_sync(queue, ^{
           NSLog(@"串行同步1-%d-%@",i,[NSThread currentThread]);
       });
   }
   for (int i = 0; i<2; i++) {
       dispatch_sync(queue, ^{
           NSLog(@"串行同步2-%d-%@",i,[NSThread currentThread]);
       });
   }

任务在当前线程下按照顺序执行,不会开启新线程。 image.png 2. 异步执行,串行队列

   NSLog(@"%@",[NSThread currentThread]);
   dispatch_queue_t queue = dispatch_queue_create("serial_queue", DISPATCH_QUEUE_SERIAL);
   for (int i = 0; i<2; i++) {
       dispatch_async(queue, ^{
           NSLog(@"串行异步1-%d-%@",i,[NSThread currentThread]);
       });
   }
    for (int i = 0; i<2; i++) {
        dispatch_async(queue, ^{
            NSLog(@"串行异步2-%d-%@",i,[NSThread currentThread]);
        });
    }

会开启一个新线程,并在当前线程下,按照顺序执行任务。 image.png 3. 同步执行,并发队列

    NSLog(@"%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i<2; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"并发同步1-%d-%@",i,[NSThread currentThread]);
        });
    }
    for (int i = 0; i<2; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"并发同步2-%d-%@",i,[NSThread currentThread]);
        });
    }

不会开启新线程,在当前线程下按顺序执行任务。 image.png 4. 异步执行,并发队列

    NSLog(@"%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i<2; i++) {
        dispatch_async(queue, ^{
            NSLog(@"并发异步1-%d-%@",i,[NSThread currentThread]);
        });
    }
    for (int i = 0; i<2; i++) {
        dispatch_async(queue, ^{
            NSLog(@"并发异步2-%d-%@",i,[NSThread currentThread]);
        });
    }

会开启新的线程,任务无顺序交替执行。 image.png

5. 同步执行,主队列

    NSLog(@"%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_sync(queue, ^{
        for(int i=0;i<2;i++){
            NSLog(@"主队列同步1-%d-%@",i,[NSThread currentThread]);
        }
    });
    NSLog(@"---%@",[NSThread currentThread]);

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) 程序崩溃,任务互相等待,造成死锁。 形成死锁的原因:因为主队列依靠主线程,所以所有的任务都在主线程中执行,首先是一个NSLog打印任务,然后是block同步任务,同步任务里面也是NSLog打印任务,所以任务在队列中的顺序是:NSLog任务1->block任务->NSLog任务2,而此时因为是同步队列,block任务需要等NSLog任务2执行完,而NSLog任务2又需要等block任务执行完,这就形成了任务互相等待,形成死锁。

6. 异步执行,主队列

    NSLog(@"%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        for(int i=0;i<2;i++){
            NSLog(@"主队列异步1-%d-%@",i,[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for(int i=0;i<2;i++){
            NSLog(@"主队列异步2-%d-%@",i,[NSThread currentThread]);
        }
    });

不会开启新线程,在主线程中串行执行任务。 image.png

总结 image.png

GCD的API

1.栅栏函数:dispatch_barrier_async

有时需要异步执行两个任务,而且第一个任务执行完之后,才能开始执行第二个任务。这样我们就需要一个相当于栅栏一样的一个方法将两个异步执行的任务给分开来,这就需要用到dispatch_barrier_async方法在两个任务值间形成栅栏。

    dispatch_queue_t queue = dispatch_queue_create("concurrent.queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"1---%@",[NSThread currentThread]);
        }
    });
    dispatch_barrier_async(queue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"barrier---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 2; ++i) {
            NSLog(@"2---%@",[NSThread currentThread]);
        }
    });

image.png

2.一次性方法:dispatch_once

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //执行代码
    });
    
    dispatch_once保证了在运行期间,block中的代码只执行一次,block中的代码默认是线程安全的,适用于单利模式。

3.延时执行方法:dispatch_after

    NSLog(@"%@",[NSThread currentThread]);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2.0秒后异步追加任务代码到主队列,并开始执行
        NSLog(@"after---%@",[NSThread currentThread]);
    });

image.png 4.计时方法:dispatch_source_t

主要用于计时操作,其原因是因为它创建的timer不依赖于RunLoop,且计时精准度比NSTimer高。

    NSLog(@"%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 3.0*NSEC_PER_SEC, 0.1*NSEC_PER_SEC);
    dispatch_source_set_event_handler(self.timer, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
    dispatch_resume(self.timer);

注意,这里要把timer设置成全局变量才会有作用。 image.png 5.快速迭代方法:dispatch_apply

dispatch_apply按照指定的次数将指定的任务追加到指定的队列中,并等待全部队列执行结束。无论是在串行队列,还是异步队列中,dispatch_apply都会等待全部任务执行完毕,类似于同步执行。

    NSLog(@"%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_queue_create("serial.queue", DISPATCH_QUEUE_SERIAL);
    dispatch_apply(3, queue, ^(size_t index) {
        NSLog(@"dispatch_apply--%ld--%@",index, [NSThread currentThread]);
    });
    NSLog(@"%@",[NSThread currentThread]);

image.png 6.监听任务方法:dispatch_group_notify

    NSLog(@"0---%@",[NSThread currentThread]);
    dispatch_group_t group =  dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"2---%@",[NSThread currentThread]);
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的异步任务1、任务2都执行完毕后,回到主线程执行任务
        NSLog(@"3---%@",[NSThread currentThread]);
    });

image.png 7.阻塞线程:dispatch_group_wait

阻塞当前线程,等待group中的任务执行完成后,才会往下继续执行任务。

    NSLog(@"0---%@",[NSThread currentThread]);
    dispatch_group_t group =  dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"1---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"2---%@",[NSThread currentThread]);
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"0---%@",[NSThread currentThread]);

image.png 8. dispatch_group_enterdispatch_group_leave

dispatch_group_enter表示一个任务追加到指定的groupdispatch_group_leave表示一个任务离开了指定的group

    NSLog(@"%@",[NSThread currentThread]);
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"1---%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"2---%@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"3---%@",[NSThread currentThread]);
    });
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"%@",[NSThread currentThread]);

首先两个异步任务会依次被加入到group中,dispatch_group_notify函数会等前面group中的任务执行完成才会执行,而dispatch_group_wait函数会阻塞当前线程,等dispatch_group_notify函数以上的任务全部执行完才会向下执行。 image.png 9.信号量:dispatch_semaphore_t

信号量是一种同步锁,主要用于控制最大并发数,可以将异步执行任务转换为同步执行任务。

    NSLog(@"-----%@",[NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    for (int i = 0; i < 5; i++) {
        dispatch_async(queue, ^{
            NSLog(@"信号量:%d------%@", i, [NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    }
    NSLog(@"-----%@",[NSThread currentThread]);
  1. dispatch_semaphore_create():创建信号量
  2. dispatch_semaphore_wait():等待信号量,调用一次,信号量减1。当信号量<0时会阻塞当前线程,根据传入的时间参数决定接下来的任务执行,如果时间参数为永久等待,则将等到有信号才继续执行下去。
  3. dispatch_semaphore_signal():释放信号量,调用一次,信号量加1。当信号量>=0会执行dispatch_semaphore_wait之后的代码。

image.png

NSOperation

NSOperation是苹果提供给开发者的一套基于GCD的多线程解决方案。NSOperation是完全面向对象,比GCD更简单易用、代码可读性也更高。

NSOperation是如何实现多线程的:

  1. 创建任务:先将需要执行的任务封装到NSOperation对象中。

  2. 创建队列:创建NSOperationQueueNSOperation需要配合NSOperationQueue来实现多线程。

  3. 将任务添加入到队列中:将NSOperation对象添加到NSOperationQueue中。 值得我们注意的是,NSOperation只是个抽象类,不能用来封装任务,实际开发时需要使用它的子类来封装任务,有三种方式来使用子类:

  4. 使用子类NSInvocationOperation

  5. 使用子类NSBlockOperation

  6. 自定义继承自NSOperation的子类,通过实现内部相应的方法来封装任务。

NSInvocationOperation

    NSLog(@"-----%@",[NSThread currentThread]);
    //创建 NSInvocationOperation 对象
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction:) object:nil];
    
    //调用 start 方法开始执行任务
    [op start];

任务会在当前线程执行,不会开启新线程。 image.png

NSBlockOperation

    NSLog(@"-----%@",[NSThread currentThread]);
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperation----1---%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"NSBlockOperation----2---%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"NSBlockOperation----3---%@", [NSThread currentThread]);
    }];
    [op start];

直接把任务放到NSBlockOperationblock中去执行,是直接在当前线程执行,不会开启新线程,而通过addExecutionBlock:方法把任务添加到NSBlockOperation中时,会开启新的线程来执行addExecutionBlock:添加的任务。 image.png

自定义NSOperation

定义一个继承自NSOperation的子类,然后重写它的main方法,之后使用这个子类来进行相关的任务的执行。

    #import "ZHOperation.h"

    @implementation ZHOperation
    - (void)main{
        NSLog(@"NSOperation的子类:%@",[NSThread currentThread]);
    }
    @end


    NSLog(@"-----%@",[NSThread currentThread]);
    ZHOperation *op = [[ZHOperation alloc] init];
    [op start];

任务在主线程执行,并不会开启新线程。 image.png

线程队列NSOperationQueue

NSOperationQueue总共有两种队列:主队列和非主队列,主队列中的任务在主线程中执行,非主队列中的任务,默认是并发执行的,会开启多线程来执行任务。

    //主队列
    NSOperationQueue *mainQueue = [NSOperationQueue mainQueue];

    //非主队列
    NSOperationQueue *otherQueue = [[NSOperationQueue alloc] init];

1.将任务添加到队列中

    NSLog(@"-----%@",[NSThread currentThread]);
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *invocationOp1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationAction) object:nil];
    NSInvocationOperation *invocationOp2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationAction) object:nil];
    
    NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"NSBlockOperation---%@", [NSThread currentThread]);
    }];
    [blockOp addExecutionBlock:^{
        NSLog(@"NSBlockOperation---%@", [NSThread currentThread]);
    }];
    [queue addOperation:invocationOp1];
    [queue addOperation:invocationOp2];
    [queue addOperation:blockOp];
    [queue addOperationWithBlock:^{
        NSLog(@"Queue Operation---%@", [NSThread currentThread]);
    }];

任务在子线程执行,开启了新的线程来执行任务。 image.png

2.最大并发数maxConcurrentOperationCount 最大并发数maxConcurrentOperationCountNSOperationQueue用来控制串行执行、并发执行的。

  • maxConcurrentOperationCount默认情况下为-1,表示不进行限制,可进行并发执行。
  • maxConcurrentOperationCount1时,队列为串行队列,任务串行执行。
  • maxConcurrentOperationCount大于1时,队列为并发队列,任务并发执行。
    NSLog(@"-----%@",[NSThread currentThread]);
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 2;
    for (int i = 0; i < 6; i++) {
        [queue addOperationWithBlock:^{ // 一个任务
            [NSThread sleepForTimeInterval:3];
            NSLog(@"max---任务%d----%@",i,[NSThread currentThread]);
        }];
    }

image.png 3. 添加任务依赖addDependency

1.addDependency:(NSOperation*)op,添加依赖,使当前任务依赖于任务op的完成。

2.removeDependency:(NSOperation*)op,移除依赖,移除当前任务对op任务的依赖。

3.property (readonly, copy) NSArray<NSOperation > dependencies,添加依赖任务的数组,即当前任务依赖于数组中的所有任务。

    NSLog(@"-----%@",[NSThread currentThread]);
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *blockOp1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blockOp1-----%@",[NSThread currentThread]);
    }];
    NSBlockOperation *blockOp2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blockOp2-----%@",[NSThread currentThread]);
    }];
    NSBlockOperation *blockOp3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blockOp3-----%@",[NSThread currentThread]);
    }];
    [blockOp1 addDependency:blockOp2];
    [blockOp2 addDependency:blockOp3];
    
    [queue addOperation:blockOp1];
    [queue addOperation:blockOp2];
    [queue addOperation:blockOp3];

image.png 4.线程之间的通信

    NSLog(@"-----%@",[NSThread currentThread]);
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperationWithBlock:^{
            for (int i = 0; i < 2; i++) {
                NSLog(@"otherqueue---%@", [NSThread currentThread]);
            }
            // 回到主线程
            [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                NSLog(@"mainqueue---%@", [NSThread currentThread]);
            }];
    }];

image.png 5.NSOperation的其他操作

1.取消操作

    //取消某个NSOperation
    [operation cancel];
    //取消某个NSOperationQueue剩余的所有NSOperation
    [queue cancelAllOperations];

2.queue暂停与恢复

    //暂停queue中所有operation
    queue.suspended = true; 
    //恢复queue中所有operation
    queue.suspended = false; 

暂停和取消操作不能作用于当前正处于运行状态的任务.

3.监听任务的完成

    NSLog(@"-----%@",[NSThread currentThread]);
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *blockOp1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"blockOp1-----%@",[NSThread currentThread]);
    }];
    [queue addOperation:blockOp1];
    blockOp1.completionBlock = ^{
        NSLog(@"completionBlock-----%@",[NSThread currentThread]);
    };

4.判断任务的状态

    //是否取消
    @property (readonly, getter=isCancelled) BOOL cancelled;
    //是否正在执行
    @property (readonly, getter=isExecuting) BOOL executing;
    //是否完成
    @property (readonly, getter=isFinished) BOOL finished;