iOS 多线程之 GCD

多线程之GCD

什么是 GCD

  • GCD 的全称是Grand Central Dispatch, 是Apple开发的一个多核编程的较新的解决方法。它主要用于优化应用程序以支持多核处理器以及其他对称多处理系统。它是一个在线程池模式的基础上执行的并行任务。在Mac OS X 10.6雪豹中首次推出,也可在IOS 4及以上版本使用。

GCD的主要用途

  • GCD可用于多核的并行运算
  • GCD会自动利用更多的CPU内核(比如双核、四核)
  • GCD会自动管理线程的生命周期(创建线程、调度任务、销毁线程)
  • 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码

GCD中的两大核心概念: 队列和任务

队列

  1. 队列的基本简介
    • 这里的队列是指任务队列, 即用来存放任务的队列, 它是一种特殊的线性表, 采用 FIFO(先进先出)的原则, 即新任务总是在队列的末尾被插入进去, 而读取任务时总是从队列的头部开始读取. 每读取一个任务, 则从队列中释放一个一个任务
  2. 队列的分类
    • 在 GCD 中 有两种队列: 串行对列和并行队列
    • 串行队列(Serial Dispatch Queue): 在串行队列中, 任务是一个接着一个执行的,一个任务执行完毕后, 才会执行下一个任务
    • 并行队列(Concurrent Dispatch Queue): 在并行队列中, 若该对列中有过个任务, 则这多个任务是并行的(同时执行)(会自动开启多个线程同时执行任务), 就像多车道一样.
    • 全局对列: 全局队列也属于并行队列
    • 主队列: 主队列也属于串行队列
  3. GCD 中队列的创建
    • 在 GCD 中可以使用dispatch_queue_create来创建对象,需要传入两个参数,第一个参数表示队列的唯一标识符,用于DEBUG,可为空;第二个参数用来识别是串行队列还是并行队列;
      DISPATCH_QUEUE_SERIAL表示串行队列,DISPATCH_QUEUE_CONCURRENT表示并行队列
    • 串行队列的创建
     //串行队列
       dispatch_queue_t serialQueue = dispatch_queue_create("com.test.serialqueue", DISPATCH_QUEUE_SERIAL);
    • 并行队列的创建
    //并行队列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.test.concurrentqueue", DISPATCH_QUEUE_CONCURRENT);
    • 全局队列
    /**
    优先级配置
    DISPATCH_QUEUE_PRIORITY_HIGH 2
    DISPATCH_QUEUE_PRIORITY_DEFAULT 0
    DISPATCH_QUEUE_PRIORITY_LOW (-2)
    DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
    
    */
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    • 主队列
     dispatch_queue_t mainQueue = dispatch_get_main_queue()

任务

  1. 任务的基本简介

    • 务就是执行操作的意思, 也就是在线程中执行的那段代码, 带 GCD 中是放在 block 中的.
  2. 任务的分类

    • 任务的执行方式分为两种:同执行和异步执行, 这二者的区别主要在于:是否等待队列的任务执行结束,以及是否具备开启新线程的能力
    • 同步执行(sync): 同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行。只能在当前线程中执行任务,不具备开启新线程的能力。
    • 异步执行(async): 异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。可以在新的线程中执行任务,具备开启新线程的能力。
    • 需要注意的是, 异步执行虽然具有开启新线程的能力, 但并不不是一定会开启新的线程, 这和任务指定的队列有关
  3. 任务的创建

    • 在全局队列中执行同步任务/异步任务
    //全局队列
    dispatch_queue_t  globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    
     //异步执行任务1
     //将任务加到全局队列(并行队列)中进行异步执行
    dispatch_async(serialQueue, ^{
    
        NSLog(@"异步任务_1");
    });
    
    //同步执行任务2
    //任务加到全局队列(并行队列)中进行同步步执行
    dispatch_sync(serialQueue, ^{
        NSLog(@"同步任务_2");
    });
    • 在主队列中执行同步任务/异步任务
    //主队列
    dispatch_queue_t  mainQueue = dispatch_get_main_queue();
    
     //异步执行任务1
     //将任务加到主队列(串行队列)中进行异步执行
    dispatch_async(serialQueue, ^{
    
        NSLog(@"异步任务_1");
    });
    
    //同步执行任务2
    //任务加到主队列(串行队列)中进行同步步执行,  会死锁, 造成程序crash
    dispatch_sync(serialQueue, ^{
        NSLog(@"同步任务_2");
    });
    • 在自定义的串行队列中执行同步任务/异步任务
     //串行队列
       dispatch_queue_t serialQueue = dispatch_queue_create("com.test.serialqueue", DISPATCH_QUEUE_SERIAL);
    
     //异步执行任务1
     //将任务加到自定义的串行队列中进行异步执行
    dispatch_async(serialQueue, ^{
    
        NSLog(@"异步任务_1");
    });
    
    //同步执行任务2
    //将任务加到自定义的串行队列中进行同步执行
    dispatch_sync(serialQueue, ^{
        NSLog(@"同步任务_2");
    });
    
    • 在自定义的并行队列中执行同步任务/异步任务
    
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.test.concurrentqueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(concurrentQueue, ^{
        for (int i = 0; i < 3; i++) {
            //将任务加到自定义的并行队列中进行异步执行
            NSLog(@"异步任务_1_____所在的线程___%@",[NSThread currentThread]);
        }
    
    });
    
     dispatch_sync(concurrentQueue, ^{
        for (int i = 0; i < 3; i++) {
            //将任务加到自定义的并行队列中进行同步执行
            NSLog(@"同步任务_2_____所在的线程___%@",[NSThread currentThread]);
        }
    
    });
  4. 任务和队列间的组合

GCD线程间的通信

在开发过程中, 我们一般都是在主线程中进行 UI的刷新, 而把一下耗时的操作放到了其他线程,如图片的下载, 文件的上传等, 当这些耗时的操作执行完后, 我们会回到主线程进行 UI 的更新, 这就用到了线程间的通信

#pragma mark - 线程间的通信
- (void)threadCommunication
{
    //全局并发队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async(queue, ^{
        //异步添加任务
        for (int i= 0; i < 10; i++) {
            [NSThread sleepForTimeInterval:2];  //模拟耗时操作
            NSLog(@"当前线程_____%d________%@",i+1, [NSThread currentThread]);  //查看当前所在的线程
        }
        //回到主线程
        dispatch_async(dispatch_get_main_queue(), ^{
            [NSThread sleepForTimeInterval:2];  //模拟耗时操作
            NSLog(@"回到主线程________%@",[NSThread currentThread]);  //查看当前所在的线程
        });
    });
}

GCD栅栏函数:dispatch_barrier_async

栅栏:比喻障碍,隔阂, 在我们开发中有时需要异步执行两组操作, 而且需要第一组操作完成后, 在执行第二组操作, 这时我们就需要一个栅栏将这个两组操作分割开来, 而 GCD 的 dispatch_barrier_async 函数就可以在两组异步操作之间形成栅栏, dispatch_barrier_async函数会等待前边追加到并发队列中的任务全部执行完毕之后,再将指定的任务追加到该异步队列中。然后在dispatch_barrier_async函数追加的任务执行完毕之后,异步队列才恢复为一般动作,接着追加任务到该异步队列并开始执行

#pragma mark - GCD栅栏函数
- (void)barrierAysnc
{
    //并发队列
    dispatch_queue_t queue = dispatch_queue_create("com.test.barrier", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        // 追加任务1
        for (int i = 0; i < 3; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"任务1的当前线程___%d___%@",i+1,[NSThread currentThread]); //查看当前所在的线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务2
        for (int i = 0; i < 3; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"任务2的当前线程___%d___%@",i+1,[NSThread currentThread]); //查看当前所在的线程
        }
    });
    
    dispatch_barrier_async(queue, ^{
        // 追加任务 barrier
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"barrier___前面的任务已经操作完成___%@",[NSThread currentThread]);//查看当前所在的线程
        }
    });
    
    dispatch_async(queue, ^{
        // 追加任务3
        for (int i = 0; i < 3; ++i) {
            [NSThread sleepForTimeInterval:2];             // 模拟耗时操作
            NSLog(@"任务3的当前线程___%d___%@",i+1,[NSThread currentThread]);  //查看当前所在的线程
        }
    });
    dispatch_async(queue, ^{
        // 追加任务4
        for (int i = 0; i < 3; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"任务4的当前线程___%d___%@",i+1,[NSThread currentThread]); //查看当前所在的线程
        }
    });

执行结果:

2018-05-12 17:41:35.207834+0800 多线程[30734:3189396] 任务2的当前线程___1___<NSThread: 0x60000027b540>{number = 3, name = (null)}
2018-05-12 17:41:35.207834+0800 多线程[30734:3189387] 任务1的当前线程___1___<NSThread: 0x60000027b500>{number = 4, name = (null)}
2018-05-12 17:41:37.209193+0800 多线程[30734:3189387] 任务1的当前线程___2___<NSThread: 0x60000027b500>{number = 4, name = (null)}
2018-05-12 17:41:37.209193+0800 多线程[30734:3189396] 任务2的当前线程___2___<NSThread: 0x60000027b540>{number = 3, name = (null)}
2018-05-12 17:41:39.214380+0800 多线程[30734:3189387] 任务1的当前线程___3___<NSThread: 0x60000027b500>{number = 4, name = (null)}
2018-05-12 17:41:39.214380+0800 多线程[30734:3189396] 任务2的当前线程___3___<NSThread: 0x60000027b540>{number = 3, name = (null)}
2018-05-12 17:41:41.215745+0800 多线程[30734:3189396] barrier___前面的任务已经操作完成___<NSThread: 0x60000027b540>{number = 3, name = (null)}
2018-05-12 17:41:43.217320+0800 多线程[30734:3189396] barrier___前面的任务已经操作完成___<NSThread: 0x60000027b540>{number = 3, name = (null)}
2018-05-12 17:41:45.218461+0800 多线程[30734:3189396] 任务3的当前线程___1___<NSThread: 0x60000027b540>{number = 3, name = (null)}
2018-05-12 17:41:45.218461+0800 多线程[30734:3189387] 任务4的当前线程___1___<NSThread: 0x60000027b500>{number = 4, name = (null)}
2018-05-12 17:41:47.221858+0800 多线程[30734:3189396] 任务3的当前线程___2___<NSThread: 0x60000027b540>{number = 3, name = (null)}
2018-05-12 17:41:47.221858+0800 多线程[30734:3189387] 任务4的当前线程___2___<NSThread: 0x60000027b500>{number = 4, name = (null)}
2018-05-12 17:41:49.226440+0800 多线程[30734:3189396] 任务3的当前线程___3___<NSThread: 0x60000027b540>{number = 3, name = (null)}
2018-05-12 17:41:49.226440+0800 多线程[30734:3189387] 任务4的当前线程___3___<NSThread: 0x60000027b500>{number = 4, name = (null)}

GCD延时执行函数:dispatch_after

  • 延迟函数是指在指定的时间之后执行某个任务, 在 GCD 中可以用 dispatch_after 函数实现, 但我们要注意的是, dispatch_after 并不是在指定的时间之后才开始执行处理, 而是在指定的时间之后将任务添加到队列中
#pragma mark - GCD延时执行函数
- (void)afterFunction
{
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 查看当前所在的线程
    NSLog(@"begin");
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        // 2.0秒后异步追加任务代码到主队列,并开始执行
        NSLog(@"after---%@",[NSThread currentThread]);  // 查看当前所在的线程
    });
}

执行结果, 注意时间的对比:

2018-05-12 17:54:22.508133+0800 多线程[30889:3200931] currentThread---<NSThread: 0x60400007f900>{number = 1, name = main}
2018-05-12 17:54:22.508379+0800 多线程[30889:3200931] begin
2018-05-12 17:54:24.699642+0800 多线程[30889:3200931] after---<NSThread: 0x60400007f900>{number = 1, name = main}

GCD一次性代码(只执行一次):dispatch_once

  • 我们在创建单例时,我们就用到了 GCD 的 dispatch_once 函数。使用 dispatch_once 函数能保证某段代码在程序运行过程中只被执行1次,并且即使在多线程的环境下,dispatch_once也可以保证线程安全。 至于为什么会只执行1次, 还需要探究一下其中的原理
#pragma mark - GCD一次性代码
- (instancetype)onceFunction {
    static HttpRequestHelper *helper;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        helper = [[HttpRequestHelper alloc] init];
    });
    return helper;
}

GCD快速迭代函数:dispatch_apply

GCD 为我们提供一个快速迭代的函数dispatch_apply, 该函数按照指定的次数将指定对的任务添加到指定的队列中,并等待全部队列执行完成

#pragma mark - GCD快速迭代函数
- (void)applyFunction
{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    NSLog(@"开始");
    dispatch_apply(10, queue, ^(size_t index) {
        
        NSLog(@"____%d______%@",index, [NSThread currentThread]);
    });
    NSLog(@"结束");
}

执行结果:

2018-05-13 12:12:22.106647+0800 多线程[33675:3687025] 开始
2018-05-13 12:12:22.106956+0800 多线程[33675:3687025] ____0______<NSThread: 0x604000063f80>{number = 1, name = main}
2018-05-13 12:12:22.107012+0800 多线程[33675:3687164] ____1______<NSThread: 0x60400026de80>{number = 3, name = (null)}
2018-05-13 12:12:22.107062+0800 多线程[33675:3687025] ____3______<NSThread: 0x604000063f80>{number = 1, name = main}
2018-05-13 12:12:22.107063+0800 多线程[33675:3687791] ____2______<NSThread: 0x60400026e300>{number = 4, name = (null)}
2018-05-13 12:12:22.107129+0800 多线程[33675:3687807] ____4______<NSThread: 0x600000271780>{number = 5, name = (null)}
2018-05-13 12:12:22.107155+0800 多线程[33675:3687164] ____5______<NSThread: 0x60400026de80>{number = 3, name = (null)}
2018-05-13 12:12:22.107214+0800 多线程[33675:3687791] ____7______<NSThread: 0x60400026e300>{number = 4, name = (null)}
2018-05-13 12:12:22.107215+0800 多线程[33675:3687025] ____6______<NSThread: 0x604000063f80>{number = 1, name = main}
2018-05-13 12:12:22.107244+0800 多线程[33675:3687807] ____8______<NSThread: 0x600000271780>{number = 5, name = (null)}
2018-05-13 12:12:22.107319+0800 多线程[33675:3687164] ____9______<NSThread: 0x60400026de80>{number = 3, name = (null)}
2018-05-13 12:12:22.109038+0800 多线程[33675:3687025] 结束
  • 从执行结果中可以看出, 0-9的打印顺序不定, 但会等待任务执行完成,才会执行之后的操作, 在demo中可以发现 结束是最后打印的
  • 因为是在并发队列中异步队执行任务,所以各个任务的执行时间长短不定,最后结束顺序也不定。但是结束一定在最后执行。这是因为dispatch_apply函数会等待全部任务执行完毕。

GCD队列组函数:dispatch_group

  • 当我们遇见两个或者多个异步耗时任务,然后需要在这两个或者多个异步耗时任务执行完毕后回到主线程进行其他操作时, 我们就需要用到GCD的队列组

    • 调用队列组的 dispatch_group_async 先把任务放到队列中,然后将队列放入队列组中
    • 调用队列组的 dispatch_group_notify 回到指定线程执行任务。或者使用 dispatch_group_wait 回到当前线程继续向下执行(会阻塞当前线程)
  • dispatch_group_notify

#pragma mark - dispatch_group_notify 监听 group 中任务的完成状态,当所有的任务都执行完成后,追加任务到 group 中,并执行任务。
- (void)groupNotifyFunction
{
    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, ^{
        //添加任务1
        for (int i = 0; i < 3; i++) {
             [NSThread sleepForTimeInterval:2];  //模拟耗时操作
            NSLog(@"耗时任务1_______%d_______%@",i+1,[NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, queue, ^{
        //添加任务2
        for (int i = 0; i < 3; i++) {
            [NSThread sleepForTimeInterval:2];  //模拟耗时操作
            NSLog(@"耗时任务2_______%d_______%@",i+1,[NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, queue, ^{
        //添加任务3
        for (int i = 0; i < 3; i++) {
            [NSThread sleepForTimeInterval:2];  //模拟耗时操作
            NSLog(@"耗时任务3_______%d_______%@",i+1,[NSThread currentThread]);
        }
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        //前面的耗时任务完成后, 回到主线程执行其他任务
        for (int i = 0; i < 3; i++) {
            [NSThread sleepForTimeInterval:2];  //模拟耗时操作
            NSLog(@"回到主线程_______%d_______%@",i+1,[NSThread currentThread]);
        }
    });
    
}

执行结果:

2018-05-13 12:40:49.967956+0800 多线程[33974:3727544] 耗时任务1_______1_______<NSThread: 0x604000277e00>{number = 4, name = (null)}
2018-05-13 12:40:49.967955+0800 多线程[33974:3727543] 耗时任务2_______1_______<NSThread: 0x6040002788c0>{number = 3, name = (null)}
2018-05-13 12:40:49.967956+0800 多线程[33974:3727542] 耗时任务3_______1_______<NSThread: 0x60000027c240>{number = 5, name = (null)}
2018-05-13 12:40:51.968717+0800 多线程[33974:3727542] 耗时任务3_______2_______<NSThread: 0x60000027c240>{number = 5, name = (null)}
2018-05-13 12:40:51.968717+0800 多线程[33974:3727543] 耗时任务2_______2_______<NSThread: 0x6040002788c0>{number = 3, name = (null)}
2018-05-13 12:40:51.968717+0800 多线程[33974:3727544] 耗时任务1_______2_______<NSThread: 0x604000277e00>{number = 4, name = (null)}
2018-05-13 12:40:53.969729+0800 多线程[33974:3727544] 耗时任务1_______3_______<NSThread: 0x604000277e00>{number = 4, name = (null)}
2018-05-13 12:40:53.969731+0800 多线程[33974:3727543] 耗时任务2_______3_______<NSThread: 0x6040002788c0>{number = 3, name = (null)}
2018-05-13 12:40:53.969731+0800 多线程[33974:3727542] 耗时任务3_______3_______<NSThread: 0x60000027c240>{number = 5, name = (null)}
2018-05-13 12:40:55.970400+0800 多线程[33974:3727474] 回到主线程_______1_______<NSThread: 0x604000064280>{number = 1, name = main}
2018-05-13 12:40:57.970927+0800 多线程[33974:3727474] 回到主线程_______2_______<NSThread: 0x604000064280>{number = 1, name = main}
2018-05-13 12:40:59.973529+0800 多线程[33974:3727474] 回到主线程_______3_______<NSThread: 0x604000064280>{number = 1, name = main}
  • dispatch_group_wait
#pragma mark - dispatch_group_wait 暂停当前线程(阻塞当前线程),等待指定的 group 中的任务执行完成后,才会往下继续执行。
- (void)groupWaitFunction
{
    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, ^{
        //添加任务1
        for (int i = 0; i < 3; i++) {
            [NSThread sleepForTimeInterval:2];  //模拟耗时操作
            NSLog(@"耗时任务2_______%d_______%@",i+1,[NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, queue, ^{
        //添加任务2
        for (int i = 0; i < 3; i++) {
            [NSThread sleepForTimeInterval:2];  //模拟耗时操作
            NSLog(@"耗时任务2_______%d_______%@",i+1,[NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, queue, ^{
        //添加任务3
        for (int i = 0; i < 3; i++) {
            [NSThread sleepForTimeInterval:2];  //模拟耗时操作
            NSLog(@"耗时任务3_______%d_______%@",i+1,[NSThread currentThread]);
        }
    });
    
    //等待前面的耗时任务执行完
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    NSLog(@"前面的耗时任务执行完成后, 才会执行该打印的任务");
}

执行结果:

2018-05-13 12:49:13.280444+0800 多线程[34084:3740069] 耗时任务3_______1_______<NSThread: 0x6040004634c0>{number = 5, name = (null)}
2018-05-13 12:49:13.280444+0800 多线程[34084:3740066] 耗时任务2_______1_______<NSThread: 0x60000066a100>{number = 3, name = (null)}
2018-05-13 12:49:13.280444+0800 多线程[34084:3740067] 耗时任务2_______1_______<NSThread: 0x604000278e00>{number = 4, name = (null)}
2018-05-13 12:49:15.282885+0800 多线程[34084:3740066] 耗时任务2_______2_______<NSThread: 0x60000066a100>{number = 3, name = (null)}
2018-05-13 12:49:15.282885+0800 多线程[34084:3740067] 耗时任务2_______2_______<NSThread: 0x604000278e00>{number = 4, name = (null)}
2018-05-13 12:49:15.282925+0800 多线程[34084:3740069] 耗时任务3_______2_______<NSThread: 0x6040004634c0>{number = 5, name = (null)}
2018-05-13 12:49:17.284434+0800 多线程[34084:3740069] 耗时任务3_______3_______<NSThread: 0x6040004634c0>{number = 5, name = (null)}
2018-05-13 12:49:17.284434+0800 多线程[34084:3740066] 耗时任务2_______3_______<NSThread: 0x60000066a100>{number = 3, name = (null)}
2018-05-13 12:49:17.284434+0800 多线程[34084:3740067] 耗时任务2_______3_______<NSThread: 0x604000278e00>{number = 4, name = (null)}
2018-05-13 12:49:17.284644+0800 多线程[34084:3740004] 前面的耗时任务执行完成后, 才会执行该打印的任务

GCD信号量函数:dispatch_semaphore

  • 信号量就是一种可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,则可告知系统按照我们指定的信号量数量来执行多个线程。当一个信号量被通知 ,信号量就会加1,当一个信号等待,信号总量就减1,当减到信号量小于0时,线程会被阻塞,信号量不会在减了。直到信号量大于0时,线程会再次启动执行
  • 关于信号量有以下三个函数

    dispatch_semaphore_create 创建一个信号量,设置一个初始值

    dispatch_semaphore_signal 发送一个信号,信号通知,信号量+1

    dispatch_semaphore_wait 等待信号,信号量-1

以下函数是对信号量的测试, 其实个人记得信号量和 NSOperation 的最大并发数是一致的

#pragma mark - 信号量函数
- (void)signalFunction
{
    //crate的value表示,最多几个资源可访问
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
    dispatch_queue_t quene = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //任务1
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"开始执行任务1");
        [NSThread sleepForTimeInterval:2];
        NSLog(@"任务1执行完毕");
        dispatch_semaphore_signal(semaphore);
    });
    //任务2
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"开始执行任务2");
        [NSThread sleepForTimeInterval:2];
        NSLog(@"任务2执行完毕");
        dispatch_semaphore_signal(semaphore);
    });
    //任务3
    dispatch_async(quene, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"开始执行任务3");
        [NSThread sleepForTimeInterval:2];
        NSLog(@"任务3执行完毕");
        dispatch_semaphore_signal(semaphore);
    });

}

执行结果:

  • 当信号量为2时, 执行结果如下
2018-05-14 21:49:54.446891+0800 多线程[37228:4083451] 开始执行任务1
2018-05-14 21:49:54.446924+0800 多线程[37228:4083452] 开始执行任务2
2018-05-14 21:49:56.450258+0800 多线程[37228:4083452] 任务2执行完毕
2018-05-14 21:49:56.450277+0800 多线程[37228:4083451] 任务1执行完毕
2018-05-14 21:49:56.450420+0800 多线程[37228:4083448] 开始执行任务3
2018-05-14 21:49:58.451525+0800 多线程[37228:4083448] 任务3执行完毕
  • 当信号量为1时, 执行结果如下:
2018-05-14 21:52:42.675361+0800 多线程[37304:4088798] 开始执行任务1
2018-05-14 21:52:44.681162+0800 多线程[37304:4088798] 任务1执行完毕
2018-05-14 21:52:44.681367+0800 多线程[37304:4088803] 开始执行任务2
2018-05-14 21:52:46.685073+0800 多线程[37304:4088803] 任务2执行完毕
2018-05-14 21:52:46.685253+0800 多线程[37304:4088795] 开始执行任务3
2018-05-14 21:52:48.686322+0800 多线程[37304:4088795] 任务3执行完毕
  • 当信号量为3时, 执行结果如下:
2018-05-14 21:53:36.070748+0800 多线程[37340:4090446] 开始执行任务1
2018-05-14 21:53:36.070748+0800 多线程[37340:4090444] 开始执行任务2
2018-05-14 21:53:36.070776+0800 多线程[37340:4090445] 开始执行任务3
2018-05-14 21:53:38.074507+0800 多线程[37340:4090444] 任务2执行完毕
2018-05-14 21:53:38.074507+0800 多线程[37340:4090445] 任务3执行完毕
2018-05-14 21:53:38.074507+0800 多线程[37340:4090446] 任务1执行完毕

以上只是定义 GCD 的一般用法做了一个简单的总结, 在实际开发中, 需要结合具体的业务逻辑来灵活的运用多线程.

猜你喜欢

转载自www.cnblogs.com/canfixme/p/9010714.html