iOS多线程大全

iOS多线程技术方案

技术方案/使用频率 简介 语言 线程生命周期
Pthread/几乎不用 一套多线程API适用Unix/Linux/Windows
跨平台、可移植、使用难度大
C语言 程序员管理
NSthread/偶尔使用 使用更加面向对象
简单易用,可直接操作线程对象
实际是对Pthread的封装
OC语言 程序员管理
GCD/经常使用 替代NSThread的线程技术
充分利用设备的多核
C语言 自动管理
NSOperation/经常使用 基于GCD(底层为GCD)
比GCD多了一些更加简单实用的功能
使用更加面向对象
OC语言 自动管理

Pthread

// 不带参数
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //创建子线程
    pthread_t pthread;
    
    // 1、线程编号地址
    // 2、线程属性
    // 3、线程要执行的函数void *(*)(void *)
    // 4、要执行的函数参数
    // pthread_create(pthread_t  _Nullable *restrict _Nonnull, const pthread_attr_t *restrict _Nullable, void * _Nullable (* _Nonnull)(void * _Nullable), void *restrict _Nullable)
    
    int result = pthread_create(&pthread, NULL, demo, NULL);
    // result 0: 成功, !0: 失败
    if (result == 0) {
        NSLog(@"成功");
    } else {
        NSLog(@"失败");
    }
}

void *demo(void *param){
    NSLog(@"hello %@", [NSThread currentThread]);
    return  NULL;
}
复制代码

Snip20210907_2.png

// 带参数
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    pthread_t pthread;
    NSString *name = @"xyh";
    int result = pthread_create(&pthread, NULL, demo, (__bridge void *)(name));
    if (result == 0) {
        NSLog(@"成功");
    } else {
        NSLog(@"失败");
    }
}

void *demo(void *param){
    NSString *name = (__bridge NSString *)param;
    NSLog(@"hi %@", name);
    NSLog(@"hello %@", [NSThread currentThread]);
    return  NULL;
}
复制代码

Snip20210907_3.png

NSThread

NSThread最常用的查看线程的方法就是[NSThread currentThread]

线程创建

  • 方式一:
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];
[thread start];
复制代码
  • 方式二:
[NSThread detachNewThreadSelector:@selector(demo) toTarget:self withObject:nil];
复制代码
  • 方式三:
NSThread *thread = [[NSThread alloc] initWithBlock:^{
   NSLog(@"%@", [NSThread currentThread]);
}];
[thread start];
复制代码

方式二一句话能解决的创建线程的方法为什么还会衍生出方式一和方式三?
就是因为可以拿到线程的状态做一些其他操作。

线程状态(新建、就绪、运行、阻塞、死亡

59D9AE2F97D3DC6C6F3AEDBF2732C5D2.png

  当调用[thread start]方法,就说明该线程对象处于就绪状态,只要线程对象处于就绪状态,那么该线程对象就被放入了可调度线程池中,在就绪状态到运行状态是由CPU进行快速跳转执行的,没执行完的线程由内存保存,下次跳转继续执行;当线程对象处于阻塞状态时,线程对象从可调度线程池中移除至内存中,等待该线程对象为就绪状态时,再次被添加到可调度线程池中,然后等待被调度执行。

// 新建状态
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(demo) object:nil];

// 线程的一些属性
  // 设置线程的名字: name
  // 设置优先级:threadPriority
  // 线程优先级:qualityOfService

// 线程的一些相关方法
  // +(NSThread *)mainThread; 获得主线程
  // -(BOOL)isMainThread; 对象方法判断是否是主线程
  // +(BOOL)isMainThread; 类方法判断是否是主线程

// 就绪状态
[thread start];

- (void)demo {
    for (int i = 0; i < 20; i++) {
        NSLog(@"%d", i);
        if (i == 5) {
            // 阻塞状态
            [NSThread sleepForTimeInterval:3];
        }
        if (i == 10) {
            // 线程退出    死亡状态
            [NSThread exit];
        }
    }
}
复制代码

线程安全问题(老生常谈的读写问题)

B00339C8-5C3C-42E1-8856-1CDA9B322F70.png

C464D87F-A166-4307-BEC9-38341FFD9B4B.png
对同一线程访问资源时的读写操作加上互斥锁,防止多个线程同时读写数据造成的数据混乱也就是数据安全问题 ,互斥锁的作用就是为了保证线程同步,同时加锁会影响程序的性能,但是为了保证数据的正确性也是需要加锁的,互斥锁锁住的代码越少越好。

#import "ViewController.h"

@interface ViewController ()

// 总票数
@property (nonatomic, assign) int ticketsCount;

@property (nonatomic, strong) NSObject *obj;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.ticketsCount = 10;
    self.obj = [NSObject new];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {

    // 模拟买票窗口1
    NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(sellTickets) object:nil];
    [thread1 start];

    // 模拟买票窗口2
    NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(sellTickets) object:nil];
    [thread2 start];

}

// 线程是不安全的
// 模拟卖票的方法

- (void)sellTickets {
    while (YES) {
        // 模拟耗时
        [NSThread sleepForTimeInterval:1.0];
        
        // 任意一个对象内部都有一把锁
        // 加锁会影响程序的性能

        // 互斥锁
        // 线程同步

        @synchronized (self.obj) {
            // 判断还有没有票
            if (self.ticketsCount > 0) {
                self.ticketsCount = self.ticketsCount - 1;
                NSLog(@"剩余%d张票", self.ticketsCount);
            } else {
                NSLog(@"来晚了,票没了");
                break;
            }
        }
    }
}
@end
复制代码

GCD

  • GCD的两个核心

  1、任务:执行什么操作
  2、队列:用来存放任务

  • GCD使用的两个步骤

  1、创建任务:确定要做的事情
  2、将任务添加到队列中

    - GCD会自动将队列中的任务取出,放到对应的线程中执行

    - 任务取出遵循队列FIFO原则,先进先出,后进后出

  • GCD的执行任务函数

  1、同步执行:dispatch_sync(dispatch_queue_t  _Nonnull queue, ^(void)block)
  2、异步执行:dispatch_async(dispatch_queue_t  _Nonnull queue, ^(void)block)

  • GCD的队列类型

  1、并行队列(DISPATCH_QUEUE_CONCURRENT):

    - 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)

    - 并发功能只有在异步函数(dispatch_asyc)下才有效

  2、串行队列(DISPATCH_QUEUE_SERIAL):

    - 让任务一个接着一个执行(一个执行完毕,再执行下一个任务)

  3、主队列(全局串行队列 dispatch_get_main_queue()

  4、全局队列(本质是并发队列 dispatch_get_global_queue(0, 0)

  1. 串行队列 同步执行 不开线程 顺序执行
- (void)demo1 {

    // 串行队列
    dispatch_queue_t queue = dispatch_queue_create("xyh", DISPATCH_QUEUE_SERIAL);
    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"hello %d %@", i, [NSThread currentThread]);
        });
    }
}
复制代码

Snip20210907_4.png

  1. 串行队列 异步执行 开新线程 顺序执行
- (void)demo2 {

    // 串行队列
    dispatch_queue_t queue = dispatch_queue_create("xyh", DISPATCH_QUEUE_SERIAL);
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"hello %d %@", i, [NSThread currentThread]);
        });
    }
}
复制代码

Snip20210907_5.png

  1. 并行队列 同步执行 不开线程 顺序执行
- (void)demo3 {

    // 并行队列
    dispatch_queue_t queue = dispatch_queue_create("xyh", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i++) {
        dispatch_sync(queue, ^{
            NSLog(@"hello %d %@", i, [NSThread currentThread]);
        });
    }
}
复制代码

Snip20210907_6.png

4.并行队列 异步执行 开新线程 无序执行

- (void)demo4 {

    // 并行队列
    dispatch_queue_t queue = dispatch_queue_create("xyh", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i++) {
        dispatch_async(queue, ^{
            NSLog(@"hello %d %@", i, [NSThread currentThread]);
        });
    }
}
复制代码

image.png

5.主队列 同步执行 死锁

- (void)demo5 {

    NSLog(@"begin");
    for (int i = 0; i < 10; i++) {
        // 死锁
        dispatch_sync(dispatch_get_main_queue(), ^{
            NSLog(@"hello %d %@", i, [NSThread currentThread]);
        });
    }
    NSLog(@"end");
}
复制代码

image.png
  死锁原因:主队列有一个特点是当主线程在执行代码时,暂时不调度任务,等主线程执行结束后再执行任务,当主队列执行到上述代码时,主队列等着主线程执行完代码后再调度任务,在同步执行操作中,如果一个任务没有执行,就继续等待第一个任务执行完成之后再执行下一个任务此时会造成互相等待,程序无法继续往下执行。

6.主队列 异步执行 主线程,顺序执行

- (void)demo6 {
    for (int i = 0; i < 10; i++) {
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"hello %d %@", i, [NSThread currentThread]);
        });
    }
}
复制代码

image.png

7.全局队列 执行操作同并行队列 暂不赘述

8.GCD 队列-执行 总结
Snip20210907_11.png

9.Barrier(栅栏函数 阻塞)

  • 在多个异步操作完成之后,统一对非线程安全的对象进行更新操作
  • 适合大规模的I/O操作
  • 当访问数据库或文件时,跟新数据不能和其他跟新或读取操作在统一时间执行可以使用dispatch_barrier_async解决
- (void)downloadImage:(int)index {

    // 并发队列
    dispatch_queue_t queue = dispatch_queue_create("xyh", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{

        // 模拟下载图片
        NSString *fileName = [NSString stringWithFormat:@"%02d.jpg", index % 10 + 1];
        NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
        UIImage *img = [UIImage imageWithContentsOfFile:path];

        // 等待队列中所有的任务执行完成,才会执行barrier中的代码
        dispatch_barrier_async(queue, ^{
            [self.photoList addObject:img];
            NSLog(@"保存图片 %@  %@", fileName, [NSThread currentThread]);
        });
        NSLog(@"图片下载完成 %@  %@", fileName, [NSThread currentThread]);
    });
}
复制代码

10.延迟操作(dispatch_after())

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    NSLog(@"task"); // 2秒后打印
});
复制代码
  1. 一次性执行(dispatch_once())

一次性执行是线程安全的,在创建单列中有很好的使用

单例和懒加载的机制有点类似,懒加载是保证该类中只有一份,单例是保证整个项目中只有一份

@interface NetworkTools : NSObject

// 单例的方法
+ (instancetype)sharedNetworkTools;

+ (instancetype)sharedNetworkToolsOnce;

@end

#import "NetworkTools.h"

@implementation NetworkTools

+ (instancetype)sharedNetworkTools {
    static id instance = nil;
    // 线程同步,保证线程安全
    @synchronized (self) {
        if (instance == nil) {
            instance = [[self alloc] init];
        }
    }
    return instance;
}

+ (instancetype)sharedNetworkToolsOnce {
    static id instance = nil;
    // dispatch_once 线程安全的
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (instance == nil) {
            instance = [[self alloc] init];
        }
    });
    return instance;
}
@end

//***********************战术分割线******************************

- (void)shareDemo {
    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
    for (int i = 0; i < 10000; i++) {
        [NetworkTools sharedNetworkTools];
    }
    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
    NSLog(@"加锁%f", end - start);
}


- (void)shareDemoOnce {
    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();
    for (int i = 0; i < 10000; i++) {
        [NetworkTools sharedNetworkToolsOnce];
    }
    CFAbsoluteTime end = CFAbsoluteTimeGetCurrent();
    NSLog(@"once%f", end - start);
}

复制代码

一次性执行单例的效率要高于加互斥锁的单例

image.png

12.调度组

dispatch_group_t 调度组

dispatch_group_async()调度组的异步执行

dispatch_group_notify()监听调度组任务完成后,追加到调度组中的任务才执行

dispatch_group_enter()一个任务追加到调度组,执行一次,调度组任务数追加一次

dispatch_group_leave()一个任务离开了调度组,执行一次,调度组任务数较少一次

dispatch_group_wait()调度组的任务数为0时进入

- (void)demo1 {

    // 创建组
    dispatch_group_t group = dispatch_group_create();

    // 队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

    // 下载第一首歌曲
    dispatch_group_async(group, queue, ^{
        NSLog(@"正在下载第一个歌曲");
        [NSThread sleepForTimeInterval:1.0];
    });
    
    //************** 等价上面*************
    // dispatch_group_enter(group);
    // dispatch_async(queue, ^{
    //     NSLog(@"正在下载第一个歌曲");
    //     [NSThread sleepForTimeInterval:1.0];
    //     dispatch_group_leave(group);
    // });

    // 下载第二首歌曲
    dispatch_group_async(group, queue, ^{
        NSLog(@"正在下载第二个歌曲");
        [NSThread sleepForTimeInterval:2.0];
    });

    // 下载第三首歌曲
    dispatch_group_async(group, queue, ^{
        NSLog(@"正在下载第三个歌曲");
        [NSThread sleepForTimeInterval:3.0];
    });

    // 当三个异步任务都执行完毕,才执行
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"over %@", [NSThread currentThread]);
    });
}
复制代码

image.png

13.信号量 dispatch_semaphore

  • 保持线程同步,将异步执行任务转换为同步执行任务
  • 保证线程安全,为线程加锁

1、创建信号量 dispatch_semaphore_create(long value)

  • vale为0仅创建信号量 大于0创建并发送了value个信号量

2、发送信号量 dispatch_semaphore_signal(dispatch_semaphore_t dsema)

  • 信号计数加1

3、等待信号量 dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout)

  • 信号计数减1,等待时长
- (void)waitThreadWithSemaphore {

    dispatch_queue_t currentQueue = dispatch_queue_create("current queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
    
    dispatch_async(currentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        [NSThread sleepForTimeInterval:5];
        NSLog(@"输入密码 %@", [NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });

    dispatch_async(currentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"验证密码 %@", [NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });

    dispatch_async(currentQueue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"登陆成功 %@", [NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
    NSLog(@"信号量");
}

//***********************战术分割线***************************

- (void)waitThreadWithSemaphore {

    dispatch_queue_t currentQueue = dispatch_queue_create("current queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    dispatch_async(currentQueue, ^{
        [NSThread sleepForTimeInterval:5];
        NSLog(@"输入密码 %@", [NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    dispatch_async(currentQueue, ^{
        NSLog(@"验证密码 %@", [NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

    dispatch_async(currentQueue, ^{
        NSLog(@"登陆成功 %@", [NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    NSLog(@"信号量");
}
复制代码

image.png

NSOperation

NSOperation作用

  • 是OC语言基于GCD的封装,使用起来更加简单
  • 苹果推荐使用,不用关心线程及其生命周期
  • NSOperation是一个抽象类,要使用其子类 NSInvocationOperation、NSBlockOperation、自定义operation

子类 NSInvocationOperation

NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationDemo) object:nil];
    NSLog(@"是否结束 = %d", op.isFinished);
    // start方法更新操作的状态 调用main方法
    // 不会开启新线程
    [op start]; // 不开启新线程
    [op start];
    [op start];
    NSLog(@"是否结束 = %d", op.isFinished);
    
- (void)operationDemo {
    NSLog(@"hello %@", [NSThread currentThread]);
}
复制代码

image.png

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

- (void)detachOperation {
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationDemo) object:nil];
    [op start];
}

- (void)operationDemo {
    NSLog(@"hello %@", [NSThread currentThread]);
}
复制代码

image.png
由上述的两个列子可以看出,子类NSInvocationOperation没有使用NSOperationQueue的情况下,操作是在当前线程下执行,并没有开启新的线程。

子类 NSBlockOperation

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"hello = %@", [NSThread currentThread]);
}];
    [op start];
复制代码

image.png

NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
    for (int i = 0; i < 3; i++) {
        NSLog(@"1---%@", [NSThread currentThread]);
    }
}];

// 添加操作
[op addExecutionBlock:^{
    for (int i = 0; i < 3; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"2---%@", [NSThread currentThread]);
    }
}];

// 添加操作
[op addExecutionBlock:^{
    for (int i = 0; i < 3; i++) {
        [NSThread sleepForTimeInterval:2];
        NSLog(@"3---%@", [NSThread currentThread]);
    }
}];

[op start];
复制代码

image.png
由上述的两个列子可以看出子类NSBlockOperation没有使用NSOperationQueue的情况下,如果不使用addExecutionBlock添加额外的操作任务,还是在当前线程中完成操作的,一旦调用了addExecutionBlock方法,就有可能会开启新的线程执行操作,这取决于线程的个数,开多少线程也是由系统决定的。

自定义子类 XXXOperation

自定义子类只需要继承Operation重写main方法即可,在没有没有使用NSOperationQueue的情况下,使用情况同其两个子类,不做赘述。

NSOperationQueue

NSOperationQueue有主队列([NSOperationQueue mainQueue])和非主队列[[NSOperationQueue alloc] init]之分,主队列中添加的操作只会在主队列中执行,非主队列可以自动开启线程去执行添加的操作,除此之外还可以控制是并发执行还是串行执行

//创建操作1
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationDemo1) object:nil];

//创建操作2
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationDemo2) object:nil];

//创建操作3
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"操作3 = %@", [NSThread currentThread]);
}];



//创建主队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];

[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];

- (void)operationDemo1 {
    NSLog(@"操作1 = %@", [NSThread currentThread]);
}

- (void)operationDemo2 {
    NSLog(@"操作2 = %@", [NSThread currentThread]);
}
复制代码

image.png

//创建操作1
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationDemo1) object:nil];

//创建操作2
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationDemo2) object:nil];

//创建操作3
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"操作3 = %@", [NSThread currentThread]);
}];



//创建非主队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];

[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];

- (void)operationDemo1 {
    NSLog(@"操作1 = %@", [NSThread currentThread]);
}

- (void)operationDemo2 {
    NSLog(@"操作2 = %@", [NSThread currentThread]);
}
复制代码

image.png

maxConcurrentOperationCount叫最大并发数,当值为1时,queue就会开多个线程串行执行,如下:

//创建操作1
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationDemo1) object:nil];

//创建操作2
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationDemo2) object:nil];

//创建操作3
NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"操作3 = %@", [NSThread currentThread]);
}];



//创建非主队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.maxConcurrentOperationCount = 1;

[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];

- (void)operationDemo1 {
    NSLog(@"操作1 = %@", [NSThread currentThread]);
}

- (void)operationDemo2 {
    NSLog(@"操作2 = %@", [NSThread currentThread]);
}
复制代码

image.png

操作依赖 addDependency

添加操作之间的依赖可以保证操作的先后执行顺序

NSBlockOperation * op1 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"账号 = %@", [NSThread currentThread]);
}];

NSBlockOperation * op2 = [NSBlockOperation blockOperationWithBlock:^{
    [NSThread sleepForTimeInterval:2];
    NSLog(@"密码 = %@", [NSThread currentThread]);
}];

NSBlockOperation * op3 = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"登录 = %@", [NSThread currentThread]);
}];


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

//添加操作依赖
[op2 addDependency:op1];
[op3 addDependency:op2];

[queue addOperation:op3];
[queue addOperation:op2];
[queue addOperation:op1];
复制代码

image.png

优先级queuePriority

为操作设置queuePriority,可以改变当前操作在同一队列中的执行优先级,不能代替操作依赖关系。决定了操作进入准备就绪状态下的操作之间的开始执行顺序。

op3 -> op2 -> op1

op4

此时的op2、op3都存在依赖关系,op1和op4无依赖关系,两者处于就绪状态,若op1的优先级大于op4,则开始执行顺序就是op1,再到op4,但是结束顺序无法保证。

线程间通信

C419A8C2-5B90-480B-B067-05AFF6013000.png

// 进程间通信,回到主线程更新UI
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
    NSLog(@"更新UI");
}];
复制代码

NSOperation 常用属性和方法

  1. 取消操作方法

    • - (void)cancel; 可取消操作,实质是标记 isCancelled 状态。
  2. 判断操作状态方法

    • - (BOOL)isFinished; 判断操作是否已经结束。
    • - (BOOL)isCancelled; 判断操作是否已经标记为取消。
    • - (BOOL)isExecuting; 判断操作是否正在在运行。
    • - (BOOL)isReady; 判断操作是否处于准备就绪状态,这个值和操作的依赖关系相关。
  3. 操作同步

    • - (void)waitUntilFinished; 阻塞当前线程,直到该操作结束。可用于线程执行顺序的同步。
    • - (void)setCompletionBlock:(void (^)(void))block; completionBlock 会在当前操作执行完毕时执行 completionBlock。
    • - (void)addDependency:(NSOperation *)op; 添加依赖,使当前操作依赖于操作 op 的完成。
    • - (void)removeDependency:(NSOperation *)op; 移除依赖,取消当前操作对操作 op 的依赖。
    • @property (readonly, copy) NSArray<NSOperation *> *dependencies; 在当前操作开始执行之前完成执行的所有操作对象数组。

NSOperationQueue 常用属性和方法

  1. 取消/暂停/恢复操作

    • - (void)cancelAllOperations; 可以取消队列的所有操作。
    • - (BOOL)isSuspended; 判断队列是否处于暂停状态。 YES 为暂停状态,NO 为恢复状态。
    • - (void)setSuspended:(BOOL)b; 可设置操作的暂停和恢复,YES 代表暂停队列,NO 代表恢复队列。
  2. 操作同步

    • - (void)waitUntilAllOperationsAreFinished; 阻塞当前线程,直到队列中的操作全部执行完毕。
  3. 添加/获取操作

    • - (void)addOperationWithBlock:(void (^)(void))block; 向队列中添加一个 NSBlockOperation 类型操作对象。
    • - (void)addOperations:(NSArray *)ops waitUntilFinished:(BOOL)wait; 向队列中添加操作数组,wait 标志是否阻塞当前线程直到所有操作结束
    • - (NSArray *)operations; 当前在队列中的操作数组(某个操作执行结束后会自动从这个数组清除)。
    • - (NSUInteger)operationCount; 当前队列中的操作数。
  4. 获取队列

    • + (id)currentQueue; 获取当前队列,如果当前线程不是在 NSOperationQueue 上运行则返回 nil。
    • + (id)mainQueue; 获取主队列。

参考资料:

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享