iOS开发之多线程

本文章博主和大家一块学习多线程,很自然就涉及到线程和进程,然后涵括NSThread、GCD、NSOperation!然后就是最牛叉的RunLoop和Runtime。

一、线程与进程
进程是对运行时程序的封装,是系统进行资源调度和分配的基本单位,实现了操作系统的并发。
线程是进程的子任务,是CPU调度和分配的基本单位,用于保障程序执行的实时性,实现进程内部的并发。
区别:

  • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程依赖于进程而存在。
  • 进程是资源分配的最小单位,线程是CPU调度的最小单位
  • 进程在执行过程中拥有独立的内存单元,而多个线程共享进程的内存。
  • 进程间不会相互影响 ;线程:一个线程挂掉将导致整个进程挂掉
  • 线程之间的通信更加方便,同一个进程下线程共享全局变量,静态变量等数据。

二、多线程

多线程的优点和缺点

  • 优点
    1.能适当的提高程序的执行效率
    2.能适当提高资源利用率(CPU内存利用率)
  • 缺点
    1.开启线程需要占用一定的内存空间,如果开启大量的线程,则会占用大量的内存空间,降低程序的性能。
    2.线程越多,CUP在调度线程的开销就越大。
    3.程序设计更加复杂,比如线程之间的通信,多线程的数据共享。

多线程的应用
1.使用单利模式时,可以使用GCD
2.耗时操作放入子线程处理,完成后回主线程显示。
3.从数据库读取大量数据,可开辟子线程操作。
4.处理音频、视频数据时,在子线程处理。
5.数据同步操作。

三、容易混淆的4个术语

同步和异步主要影响:能不能开启新的线程。
1.同步:只是在当前线程中执行任务,不具备开启新线程的能力。
2.异步:可以在新的线程中执行,具备开启新线程的能力。

并发和串行主要影响:任务的执行方式
并发:多个任务并发(同时)执行。
串行:一个任务执行完毕后,再执行下一个任务。

这里写图片描述

四、NSThread、NSOperation、GCD

1.NSThread
1)通过NSThread的对象方法(动态创建)

这里写图片描述

2)通过NSThread的类方法(静态创建)

这里写图片描述
NSThread的线程管理

  1. 线程创建
  2. 线程开启
  3. 线程取消
  4. 线程关闭
  5. 线程暂停
  6. 设置线程优先级

NSThread的线程通信

  1. 指定当前线程执行操作
  2. (在其他线程中)指定主线程执行操作
  3. 在主线程中)指定其他线程执行操作

2.NSOperation
2.1 NSOperation 基本使用
在iOS开发中,为了提升用户体验,我们通常会将操作耗时的操作放在主线程之外的线程进行处理。对于正常的简单操作,我们更多的选择代码更少的GCD,让我们专注于自己的业务逻辑开发。NSOperation在iOS4后也基于GCD实现,但是对于GCD来说可控性更强,并且可以加入操作依赖。
(1) 相关概念
NSOperation是对GCD的封装,其中有两个核心概念【队列+操作】
1.NSOperation本身是抽象类,只能有它的子类。
2.两大子类分别是:NSBlockOperation、NSInvocationOperation,当然你也可以自定义继承自NSOperation的类。

(2)基本使用

  • 实例化NSOperation的子类,绑定执行的操作。
  • 创建NSOperationQueue队列,将NSOperation实例添加进来。
  • 系统会自动将NSOperationQueue队列中检测取出和执行NSOperation的操作。

NSInvocationOperation类

//  01 NSInvocationOperation创建线程
/*
 第一个参数:目标对象
 第二个参数:该操作要调用的方法,最多接受一个参数
 第三个参数:调用方法传递的参数,如果方法不接受参数,那么该值传nil
 */
- (void) method1 {
    NSString * imageUrl = @"https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=482494819,3150422682&fm=200&gp=0.jpg";
    NSInvocationOperation * invocationOperation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(loadImageSource:) object:imageUrl];
//    [invocationOperation start];
    //一旦执行操作,就会调用target的sel方法, 默认情况下调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作,只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    [queue addOperation:invocationOperation];
}
//更新imageView
- (void)updateImage:(NSData *)data{
    //在主线程中更新UI
    //将二进制数据转换为图片
    UIImage *image=[UIImage imageWithData:data];
    //设置image
    self.imageView.image=image;
}

NSBlockOperation类

  • 创建NSBlockOperation对象
  • 通过addExecutionBlock:方法添加更多的操作
    这里写图片描述NSOperationQueue 控制串行执行、并发执行
    使用NSOperation Queue创建的自定义队列同时具有串行、并发执行的能力,这里的关键就是maxConcurrentOperationCount这个关键属性,叫做最大并发操作数,用来控制一个特定队列中可以有多少个操作同时参与并发执行。
  • maxConcurrentOperationCount 默认情况下为-1,表示不进行限制,可进行并发执行。
  • maxConcurrentOperationCount 为1时,队列为串行队列。只能串行执行。
  • maxConcurrentOperationCount 大于1时,队列为并发队列。

队列的取消、暂停、和恢复

// 恢复队列,继续执行
// self.queue.suspended = NO;

// 暂停(挂起)队列,暂停执行
// self.queue.suspended = YES;

// 取消队列的所有操作
[self.queue cancelAllOperations];

操作依赖

NSOperation之间可以设置依赖来保证执行顺序
这里写图片描述
不添加依赖之前op1、op2、op3的顺序是随机的

在不同queue的NSOperation之间创建依赖关系
这里写图片描述
在不加依赖之前,op1和op2的执行顺序是随机的,添加依赖后op2会在op1之后执行。

线程间的通讯

[[NSOperationQueue mainQueue] addOperationWithBlock:^{
            [self updateImage:data];
        }];

3.GCD

3.1基本概念
全称是Grand Central Dispatch,即:强大的中枢调度器,它是纯C语言的,提供了非常多强大的函数。

3.2GCD的优势:

GCD是苹果公司为多核的并行运算提出的解决方案

GCD会自动利用更多的CPU内核(比如双核、四核)

GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)

程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

3.3GCD的使用步骤:

  1. 指定任务:确定想要做的事

  2. 将任务添加到队列中:GCD会自动将队列中的任务取出,放到对应的线程中执行,任务的取出遵循队列的FIFO原则:先进先出,后进后出。

3.4GCD的队列

3.4.1 GCD的队列可以分为2大类型:

1.并发队列(Current Dispatch Queue)

2.串行队列(Serial Dispatch Queue)

3.4.2 使用dispatch_queue_create函数创建队列

dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);
其中:const char *label 列队列的名称
      dispatch_queue_attr_t attr 队列的类型
创建一个并发队列
    dispatch_queue_t queue1 = dispatch_queue_create("myQueue1", DISPATCH_QUEUE_CONCURRENT);
创建一个串行队列
     dispatch_queue_t queue2 = dispatch_queue_create("myQueue2", DISPATCH_QUEUE_SERIAL);

GCD默认意境提供了全局的并发队列,供整个应用使用,可以无需手动创建

使用dispatch_get_global_queue 函数获得全局并发队列
     //获得全局的并发队列
     dispatch_queue_t queue3 = 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 --》 后台
     第二个参数标记:是为了未来使用保留的!所以这个参数应该永远指定为0

GCD中获得串行有2种途径

1.使用dispatch_queue_create 创建串行队列,创建串行队列(队列类型传递NULL或者DISPATCH_QUEUE_SERIAL)
     dispatch_queue_t queue4 = dispatch_queue_create("com.520.queue", NULL);
2.使用主队列 (跟主线程相关联的队列),主队列时GCD自带的一种特殊的串行队列,放在主队列中的任务,都会放在主线程中执行,dispatch_get_main_queue()获得主队列
     dispatch_queue_t queue5 = dispatch_get_main_queue();

3.4.3 应用

3.4.3.1 串行队里
3.4.3.2.并发队列(Concurrent Dispatch Queue)

//
//  ViewController.m
//  TestThread
//
//  Created by denggaoqiang on 2022/9/10.
//

#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self example14];
    
}

- (void)example1 {
    
    dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"当前同步任务1一-------%@",[NSThread currentThread]);
        sleep(3);
        
    });
    NSLog(@"1111");
    dispatch_sync(queue, ^{
        NSLog(@"当前同步任务2一-------%@",[NSThread currentThread]);
        sleep(2);
        
    });
    NSLog(@"2222");
    dispatch_sync(queue, ^{
        NSLog(@"当前同步任务3一-------%@",[NSThread currentThread]);
        sleep(1);
    });
    NSLog(@"走到这里了");
    /** 打印结果
     当前同步任务1
     1111
     当前同步任务2
     2222
     当前同步任务3
     走到这里了
     */
}

- (void)example2 {
    
    dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        
        sleep(5);
        NSLog(@"当前异步任务1一-------%@",[NSThread currentThread]);
    });
    NSLog(@"1111");
    dispatch_async(queue, ^{
        
        sleep(2);
        NSLog(@"当前异步任务2一-------%@",[NSThread currentThread]);
    });
    NSLog(@"2222");
    dispatch_async(queue, ^{
        
        sleep(1);
        NSLog(@"当前异步任务3一-------%@",[NSThread currentThread]);
    });
    NSLog(@"走到这里了");
    /** 打印结果
     
     1111
     2222
     走到这里了
     当前异步任务1
     当前异步任务2
     当前异步任务3
     
     */
}

- (void)example3 {
    
    dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        sleep(3);
        NSLog(@"当前同步任务1一-------%@",[NSThread currentThread]);
    });
    NSLog(@"1111");
    dispatch_async(queue, ^{
        NSLog(@"当前异步任务2开始一-------%@",[NSThread currentThread]);
        sleep(10);
        NSLog(@"当前异步任务2结束一-------%@",[NSThread currentThread]);
        
    });
    NSLog(@"2222");
    
    dispatch_sync(queue, ^{
        sleep(2);
        NSLog(@"当前同步任务3一-------%@",[NSThread currentThread]);
        
    });
    NSLog(@"3333");
    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@"当前异步任务4一-------%@",[NSThread currentThread]);
        
    });
    NSLog(@"走到这里了");
    /** 打印结果
     当前同步任务1
     1111
     2222
     当前异步任务2开始
     当前异步任务2结束
     当前同步任务3
     3333
     走到这里了
     当前异步任务4
     */
}

- (void)example4 {
    
    dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        sleep(5);
        NSLog(@"当前同步任务1一-------%@",[NSThread currentThread]);
    });
    NSLog(@"1111");
    dispatch_sync(queue, ^{
        sleep(3);
        NSLog(@"当前同步任务2一-------%@",[NSThread currentThread]);
        
    });
    NSLog(@"2222");
    dispatch_sync(queue, ^{
        sleep(1);
        NSLog(@"当前同步任务3一-------%@",[NSThread currentThread]);
        
    });
    NSLog(@"走到这里了");
    /** 打印结果
     当前同步任务1
     1111
     当前同步任务2
     2222
     当前同步任务3
     走到这里了
     */
}

- (void)example5 {
    
    dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        sleep(5);
        NSLog(@"当前同步任务1一-------%@",[NSThread currentThread]);
    });
    NSLog(@"1111");
    dispatch_async(queue, ^{
        sleep(8);
        NSLog(@"当前异步任务2一-------%@",[NSThread currentThread]);
        
    });
    NSLog(@"2222");
    dispatch_async(queue, ^{
        sleep(3);
        NSLog(@"当前同步任务3一-------%@",[NSThread currentThread]);
        
    });
    NSLog(@"3333");
    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@"当前异步任务4一-------%@",[NSThread currentThread]);
        
    });
    NSLog(@"走到这里了");
    /** 打印结果
     1111
     2222
     3333
     走到这里了
     当前异步任务4
     当前异步任务3
     当前异步任务1
     当前异步任务2
     */
}

- (void)example6 {
    
    dispatch_queue_t queue = dispatch_queue_create("queueName", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        sleep(3);
        NSLog(@"当前同步任务1一-------%@",[NSThread currentThread]);
    });
    NSLog(@"1111");
    dispatch_async(queue, ^{
        sleep(8);
        NSLog(@"当前异步任务2一-------%@",[NSThread currentThread]);
        
    });
    NSLog(@"2222");
    dispatch_sync(queue, ^{
        sleep(3);
        NSLog(@"当前同步任务3一-------%@",[NSThread currentThread]);
        
    });
    NSLog(@"3333");
    dispatch_async(queue, ^{
        sleep(1);
        NSLog(@"当前异步任务4一-------%@",[NSThread currentThread]);
        
    });
    NSLog(@"走到这里了");
    /** 打印结果
     当前同步任务1
     1111
     2222
     当前同步任务3
     3333
     走到这里了
     当前异步任务4
     当前异步任务2
     */
}

/**总结:
 一、在同一串行队列种
 同步里面创建同步 死锁崩溃
 同步里面创建异步 正常
 异步里面创建同步 死锁崩溃
 异步里面创建异步 正常
 二、在同一并发队列
 同步里面创建同步 正常
 同步里面创建异步 正常
 异步里面创建同步 正常
 异步里面创建异步 正常
 */

-(void)example71 {
    
    //串行队列   同步里面创建同步 死锁崩溃
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"任务1...");
        NSLog(@"%@", [NSThread currentThread]);
        dispatch_sync(queue, ^{
            NSLog(@"任务2...");
            NSLog(@"%@", [NSThread currentThread]);
        });
        NSLog(@"走到这里了");
    });
    NSLog(@"结束");
    
}

-(void)example72 {
    
    //串行队列   同步里面创建异步 正常
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        NSLog(@"任务1...");
        NSLog(@"%@", [NSThread currentThread]);
        dispatch_async(queue, ^{
            NSLog(@"任务2...");
            NSLog(@"%@", [NSThread currentThread]);
        });
        NSLog(@"走到这里了");
    });
    NSLog(@"结束");
    
}

-(void)example73 {
    
    //串行队列   异步里面创建同步 死锁崩溃
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"任务1...");
        NSLog(@"%@", [NSThread currentThread]);
        dispatch_sync(queue, ^{
            NSLog(@"任务2...");
            NSLog(@"%@", [NSThread currentThread]);
        });
        NSLog(@"走到这里了");
    });
    NSLog(@"结束");
}

-(void)example74 {
    
    //串行队列   异步里面创建异步 正常
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"任务1...");
        NSLog(@"%@", [NSThread currentThread]);
        dispatch_async(queue, ^{
            NSLog(@"任务2...");
            NSLog(@"%@", [NSThread currentThread]);
        });
        NSLog(@"走到这里了");
    });
    NSLog(@"结束");
    
}

-(void)example81 {
    
    //并发队列   同步里面创建同步 正常
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        sleep(3);
        NSLog(@"任务1...");
        NSLog(@"%@", [NSThread currentThread]);
        dispatch_sync(queue, ^{
            sleep(1);
            NSLog(@"任务2...");
            NSLog(@"%@", [NSThread currentThread]);
        });
        NSLog(@"走到这里了");
    });
    NSLog(@"结束");
    
}

-(void)example82 {
    
    //并发队列   同步里面创建异步 正常
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        sleep(3);
        NSLog(@"任务1...");
        NSLog(@"%@", [NSThread currentThread]);
        dispatch_async(queue, ^{
            sleep(1);
            NSLog(@"任务2...");
            NSLog(@"%@", [NSThread currentThread]);
            NSLog(@"走到这里了");
        });
    });
    NSLog(@"结束");
}

-(void)example83 {
    
    //并发队列   异步里面创建同步 正常
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        sleep(3);
        NSLog(@"任务1...");
        NSLog(@"%@", [NSThread currentThread]);
        dispatch_sync(queue, ^{
            sleep(1);
            NSLog(@"任务2...");
            NSLog(@"%@", [NSThread currentThread]);
        });
        NSLog(@"走到这里了");
    });
    NSLog(@"结束");
}

-(void)example84 {
    
    //并发队列  异步里面创建异步 正常
    dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        sleep(3);
        NSLog(@"任务1...");
        NSLog(@"%@", [NSThread currentThread]);
        dispatch_async(queue, ^{
            sleep(1);
            NSLog(@"任务2...");
            NSLog(@"%@", [NSThread currentThread]);
            NSLog(@"走到这里了");
        });
    });
    NSLog(@"结束");
}

-(void)example91{
    
    //任务1和任务2在同一个线程里,在不同的队列里面,可以同步执行 运行正常
    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue1, ^{
        NSLog(@"%@", [NSThread currentThread]);
        NSLog(@"任务1...");
        dispatch_sync(queue2, ^{
            NSLog(@"%@", [NSThread currentThread]);
            NSLog(@"任务2...");
        });
        NSLog(@"走到这里了");
    });
    NSLog(@"结束");
    
}

-(void)example92{
    
    //任务1和任务2在两个不同线程里,并且两个不同队列,不会造成死锁 可以正常运行
    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue1, ^{
        NSLog(@"%@", [NSThread currentThread]);
        NSLog(@"任务1...");
        dispatch_async(queue2, ^{
            NSLog(@"%@", [NSThread currentThread]);
            NSLog(@"任务2...");
        });
        NSLog(@"走到这里了");
    });
    NSLog(@"结束");
    
}

-(void)example10 {
    //异步串行group
    //创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("com.sccc", DISPATCH_QUEUE_SERIAL);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, queue, ^{
        [self doSomething:^() {
            NSLog(@"任务一");
        }];
    });
    dispatch_group_async(group, queue, ^{
        [self doSomething:^() {
            NSLog(@"任务二");
        }];
    });
    dispatch_group_async(group, queue, ^{
        [self doSomething:^() {
            NSLog(@"任务三");
        }];
    });
    dispatch_group_notify(group, queue, ^{
        NSLog(@"前面的任务已完成");
    });
    
}

-(void)example11 {
    //异步串行group
    //创建串行队列
    dispatch_queue_t queue = dispatch_queue_create("com.sccc", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, queue, ^{
        [self doSomething:^() {
            NSLog(@"任务一");
        }];
    });
    dispatch_group_async(group, queue, ^{
        sleep(3);
        [self doSomething:^() {
            NSLog(@"任务二");
        }];
    });
    dispatch_group_async(group, queue, ^{
        
        [self doSomething:^() {
            NSLog(@"任务三");
        }];
    });
    dispatch_group_notify(group, queue, ^{
        NSLog(@"前面的任务已完成");
    });
    
}

- (void)doSomething:(void (^)(void))handler {
    if (handler) {
        sleep(2);
        handler();
    }
}

-(void)example12 {
    
    // 因为 dispatch_group_async 里面的任务是异步的,所以任务在执行的时候,它不会去等待 for 循环执行结束,它会直接跳过 dispatch_async 这 block 执行下一句去了,所以 dispatch_group_notify 也会很快就执行。
    
    
    // 创建一个group
    dispatch_group_t group = dispatch_group_create();
    // 创建一个队列:全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 将任务1添加到 group 中
    dispatch_group_async(group, queue, ^{
        // 模拟异步网络请求
        sleep(5);
        NSLog(@"任务一完成");
    });
    
    // 将任务2添加到 group 中
    dispatch_group_async(group, queue, ^{
        // 模拟异步网络请求
        sleep(3);
        NSLog(@"任务二完成");
    });
    
    // 任务1和任务2执行结束,回调
    dispatch_group_notify(group, queue, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"完成任务");
        });
    });
    
    
    
}

-(void)example13 {
    // 创建一个group
    dispatch_group_t group = dispatch_group_create();
    // 创建一个队列:全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 进入group
    dispatch_group_enter(group);
    
    // 模拟异步网络请求
    dispatch_async(queue, ^{
        
        sleep(3);
        // 离开group
        NSLog(@"任务一完成");
        dispatch_group_leave(group);
        
        
    });
    
    // 进入group
    dispatch_group_enter(group);
    
    // 模拟异步网络请求
    dispatch_async(queue, ^{
        
        sleep(4);
        // 离开group
        NSLog(@"任务二完成");
        dispatch_group_leave(group);
        
        
    });
    
    
    dispatch_group_notify(group, queue, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"完成任务");
        });
    });
    
}

-(void)example14{
    // 创建一个group
    dispatch_group_t group = dispatch_group_create();
    // 创建一个队列:全局队列
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    // 创建信号量,并且设置值为0
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    
    // 将任务1添加到 group 中
    dispatch_group_async(group, queue, ^{
        
        // 模拟异步网络请求
        dispatch_async(queue, ^{
            sleep(3);
            NSLog(@"任务一完成");
            
            // 每次发送信号则 semaphore 会 +1
            dispatch_semaphore_signal(semaphore);
            
        });
        // 由于是异步执行的,当 semaphore 等于 0,则会阻塞当前线程,直到执行了 block 的 dispatch_semaphore_signal,semaphore + 1,才会继续执行。
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        
    });
    
    // 将任务2添加到 group 中
    dispatch_group_async(group, queue, ^{
        
        // 模拟异步网络请求
        dispatch_async(queue, ^{
            sleep(3);
            NSLog(@"任务二完成");
            
            dispatch_semaphore_signal(semaphore);
            
        });
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    });
    
    
    dispatch_group_notify(group, queue, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"完成任务");
        });
    });
    
    
}

@end

面试题1

- (void)wbinterDemo{
    dispatch_queue_t queue = dispatch_queue_create("com.lg.cooci.cn", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        sleep(2);
        NSLog(@"1");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"2");
    });
    NSLog(@"00");
    dispatch_sync(queue, ^{ NSLog(@"3"); });
    
    NSLog(@"0");
    
    dispatch_async(queue, ^{
        NSLog(@"7");
    });
    dispatch_async(queue, ^{
        NSLog(@"8");
    });
    dispatch_async(queue, ^{
        NSLog(@"9");
    });
    //打印顺序00 1 2 3 0 7 8 9
}

面试题2

- (void)MTDemo{
    self.num = 0;
    while (self.num < 50) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            self.num++;
        });
    }
    NSLog(@"end : %ld",(long)self.num);
}
这里输出的结果会是大于等于 50,首先因为这里是 while 循环,所以 self.num 不大于等于 50 的情况下不会执行打印,然后因为是并发队列异步执行,所以这里会开启多条线程,所以就有可能 self.num 刚满足大于等于 50 的条件,跳出循环的时候别的线程正好开始执行任务,然后就会对 self.num 再次进行加加运算,所以最后的打印结果大于等于 50

面试题3

- (void)KSDemo {
for (int i= 0; i<10000; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
self.num++;
});
}
NSLog(@"end : %d",self.num);
}
这里因为判断条件是一个新的变量 i,只会循环一万次,然后因为是多线程,所以当跳出循环的时候有的线程任务并没有执行完成,所以最后的打印结果小于等于 10000

3.4.3.3.线程间的通信

  • Direct messaging:这是大家非常熟悉的**-performSelector:系列**。
  • Global variables…:直接通过全局变量、共享内存等方式,但这种方式会造成资源抢夺,涉及到线程安全问题。
  • Conditions:一种特殊的锁–条件锁,当使用条件锁使一个线程等待(wait)时,该线程会被阻塞并进入休眠状态,在另一个线程中对同一个条件锁发送信号(single),则等待中的线程会被唤醒继续执行任务。
  • Run loop sources:通过自定义Run loop sources来实现,后面的文章会单独研究Run loop。
  • Ports and sockets:通过端口和套接字来实现线程间通讯。
  • Run loop:NSMachPort

3.4.3.4.其他应用

1.延时执行
iOS常见三种延时执行方式
1)调用NSObject的方法

 [self performSelector:@selector(需要执行的方法名) withObject:nil afterDelay:2.0]

2)使用GCD函数

dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2秒后执行这里的代码..
    });

3)使用定时器

[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(需要执行的方法名) userInfo:nil repeats:NO];

2.一次性代码

 static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
       //里面的代码只会被执行一次
    });

3.队列组

有这么一个需求,首先:分别异步执行2个耗时的操作,其次:等2个异步操作都执行完毕后,再回到主线程执行操作,如果想要快速高效地实现上述需求,可以考虑用队列组。

    dispatch_group_tgroup =  dispatch_group_create();  
      
    dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
      
        // 执行1个耗时的异步操作  
      
    });  
      
    dispatch_group_async(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  
      
        // 执行1个耗时的异步操作  
      
    });  
      
    dispatch_group_notify(group,dispatch_get_main_queue(), ^{  
      
        // 等前面的异步操作都执行完毕后,回到主线程...  
      
    });  

4.快速迭代
我只想说速度非常快,要加一个__block因为block代码默认不能改外面的东西
这里写图片描述

5.barry执行任务函数

在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行。
下面的例子是在添加到队列的任务1、任务2、任务3执行完毕后,然后才执行barrier,barrier执行完毕后才执行任务4、任务5。
这里写图片描述

dispatch_barrier在并发队列中创建一个同步点,当并发队列中遇到一个 dispatch_barrier时,会延时执行该 dispatch_barrier,等待在 dispatch_barrier之前提交的任务block执行完后才开始执行,之后,并发队列继续执行后续block任务。

6.GCD与定时器

//创建一个定时器(dispatch_source_t本质上还是一个OC对象)
@property (nonatomic,strong) dispatch_source_t gcdTimer;
//定时器设置
-(void)loadTimer{
    [self pause];
    dispatch_queue_t queue = dispatch_get_main_queue();
    /*
     第一个参数:dispatch_source_type_t type为设置GCD源方法的类型,前面已经列举过了。
     第二个参数:uintptr_t handle Apple的API介绍说,暂时没有使用,传0即可。
     第三个参数:unsigned long mask Apple的API介绍说,使用DISPATCH_TIMER_STRICT,会引起电量消耗加剧,毕竟要求精确时间,所以一般传0即可,视业务情况而定。
     第四个参数:dispatch_queue_t _Nullable queue 队列,将定时器事件处理的Block提交到哪个队列,可以传Null,默认为全局队列。
     */
    self.gcdTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    //将定时器写成属性,是因为内存管理的原因,使用了dispatch_source_create方法,这种方法GCD是不会帮你管理内存的。
    //设置定时器的各种属性
    /*
     第二个参数:dispatch_time_t start, 定时器开始时间,类型为 dispatch_time_t,其API的abstract标明可参照dispatch_time()和dispatch_walltime(),同为设置时间,但是后者为“钟表”时间,相对比较准确,所以选择使用后者。dispatch_walltime(const struct timespec *_Nullable when, int64_t delta),参数when可以为Null,默认为获取当前时间,参数delta为增量,即获取当前时间的基础上,增加X秒的时间为开始计时时间,此处传0即可。
     第三个参数:uint64_t interval,定时器间隔时长,由业务需求而定。
     第四个参数:uint64_t leeway, 允许误差,此处传0即可。
     */
    dispatch_time_t start = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0*NSEC_PER_SEC));
    uint64_t interval = (uint64_t)(5.0*NSEC_PER_SEC);
    dispatch_source_set_timer(self.gcdTimer, start, interval, 0);
    //设置回调
    __weak typeof(self) weakSelf = self;
    dispatch_source_set_event_handler(self.gcdTimer, ^{
        //定时器需要执行的操作
        [weakSelf timerAction];
    });
    //启动定时器(默认是暂停)
    dispatch_resume(self.gcdTimer);
    
}
-(void)pause{
    if (self.gcdTimer) {
        dispatch_cancel(self.gcdTimer);
        self.gcdTimer = nil;
    }
}
- (void)timerAction
{
// 做你想做的操作
}

关于取消 Timer,另外一个很重要的注意事项,
dispatch_suspend 之后的 Timer,是不能被释放的!下面的代码会引起崩溃:

- (void)stopTimer
{
    dispatch_suspend(_gcdTimer);
    _gcdTimer = nil; // EXC_BAD_INSTRUCTION 崩溃
}

因此使用 dispatch_suspend 时,Timer 本身的实例需要一直保持。使用 dispatch_source_cancel 则没有这个限制:

- (void)stopTimer
{
    dispatch_source_cancel(_gcdTimer);
    _gcdTimer = nil; // OK
}

dispatch_suspend 和 dispatch_resume 应该是成对出现的,两者分别会减少和增加 dispatch 对象的挂起计数,但是没有 API 获取当前是挂起还是执行状态,所以需要自己记录,否则会出现Crash。

RunLoop依赖性‌:GCD定时器不依赖于RunLoop,因此时间更加准确。相比之下,NSTimer依赖于RunLoop,可能会导致定时任务不准时‌。

使用注意总结:
1、循环引用:因为dispatch_source_set_event_handler回调是个block,在添加到source的链表上时会执行copy并被source强引用,如果block里持有了self,self又持有了source的话,就会引起循环引用。正确的方法是使用weak+strong或者提前调用dispatch_source_cancel取消timer。
2、dispatch_resume和dispatch_suspend调用次数需要平衡,如果重复调用dispatch_resume则会崩溃,因为重复调用会让dispatch_resume代码里if分支不成立,从而执行了DISPATCH_CLIENT_CRASH(“Over-resume of an object”)导致崩溃。
3、source在suspend状态下,如果直接设置source = nil或者重新创建source都会造成crash。正确的方式是在resume状态下调用dispatch_source_cancel(source)释放当前的source。

@interface YourClass ()
{
    dispatch_source_t _timer;
    BOOL _timerRunning;
}
 
- (void)startTimer;
- (void)stopTimer;
 
@end
 
@implementation YourClass
 
- (void)startTimer {
    if (!_timerRunning) {
        _timerRunning = YES;
        // 创建一个定时器,这里以5秒为例
        _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
        if (_timer) {
            // 设置定时器的开始时间和间隔
            uint64_t start = dispatch_walltime(NULL, 0);
            uint64_t interval = 5ull * NSEC_PER_SEC;
            dispatch_source_set_timer(_timer, start, interval, 0);
            
            // 设置定时器的事件处理块
            dispatch_source_set_event_handler(_timer, ^{
                // 定时器触发时的操作
                NSLog(@"Timer fired!");
            });
            
            // 启动定时器
            dispatch_resume(_timer);
        }
    }
}
 
- (void)stopTimer {
    if (_timerRunning) {
        _timerRunning = NO;
        if (_timer) {
            // 停止并释放定时器
            dispatch_source_cancel(_timer);
            _timer = NULL;
        }
    }
}
 
@end
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐