美文网首页
OC 多线程之 GCD知识从基础到进阶 (1)

OC 多线程之 GCD知识从基础到进阶 (1)

作者: 浮生随笔 | 来源:发表于2018-07-30 16:42 被阅读25次

OC 多线程之 GCD知识从基础到进阶 (1)

概念认知

基本概念:

  • GCD 全称 (Grand Center Dispatch),翻译成通俗中文“牛B的中心调度机制”

  • 线程:执行任务调度的最小单位

  • 任务:就是一段代码,GCD中就是block中的内容。执行任务有两种方式:同步执行(sync)和异步执行(async)。两者的主要区别是:是否等待队列的任务执行结束,以及是否具备开启新线程的能力

  • 队列:用来存放任务的线性结构,采用 FIFO(先进先出)的原则。

进阶概念:

  • 同步:

    • 同步添加任务到指定的队列中,在添加的任务执行结束之前,会一直等待,直到队列里面的任务完成之后再继续执行后续任务。
    • 只能在当前线程中执行任务,不具备开启新线程的能力。
  • 异步:

    • 异步添加任务到指定的队列中,它不会做任何等待,可以继续执行任务。
    • 可以在新的线程中执行任务,具备开启新线程的能力。
  • 串行队列: 队列中的任务 按添加顺序开始执行

  • 并发队列: 队列中的任务可同时开始执行,可以开辟多个线程

  • 主队列(等待主线程内的任务完成之后再利用主线程执行任务)

  • 全局并发队列:本质上就是一个apple默认的并发队列

GCD 使用步骤

  • 创建队列:

    // 串行队列的创建方法
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_SERIAL);
    // 并发队列的创建方法
    dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
    // 主队列的获取方法
    dispatch_queue_t queue = dispatch_get_main_queue();
    // 全局并发队列的获取方法
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
  • 创建任务:

    // 同步执行任务创建方法
    dispatch_sync(queue, ^{
        // 这里放同步执行任务代码
    });
    // 异步执行任务创建方法
    dispatch_async(queue, ^{
        // 这里放异步执行任务代码
    });
    

串行并发队列 + 同步异步组合

1.同步执行 + 并发队列
2.异步执行 + 并发队列
3.同步执行 + 串行队列
4.异步执行 + 串行队列
5.同步执行 + 主队列
6.异步执行 + 主队列

各种组合:


串行并发队列+同步异步组合

问题:

  • 死锁:在主线程主队列执行同步任务就会造成线程死锁。

  • 主队列中的任务一定是在主线程中执行的

  • 全局并发队列中的任务可以开辟新线程,但不一定会开辟

GCD 线程间通信

// GCD的线程间的通信 : 常用代码
- (void)GCDDemo
{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"正在努力下载... %@",[NSThread currentThread]);
        // 如果下载结束回到主线程更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"下载完成,正在更新UI... %@",[NSThread currentThread]);
        });
    });
}

GCD 的其他方法

GCD 栅栏方法:dispatch_barrier_async

对于栅栏方法的理解,我觉得下面的图片很形象:


dispatch_queue_t queue = dispatch_queue_create("net.bujige.testQueue", DISPATCH_QUEUE_CONCURRENT);
    
dispatch_async(queue, ^{
    // 追加任务1
    for (int i = 0; i < 2; ++i) {
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"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, ^{
    // 追加任务2
    for (int i = 0; i < 2; ++i) {
        [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
        NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
    }
});

虽然是异步任务 栅栏也能把这两个任务给有先后顺序的隔离开来。

GCD 延时执行方法:dispatch_after

这里需要注意的是,延迟的时间是指的 延迟将任务添加到队列中。所以这个时间并不是特别准确的

/**
 * 延时执行方法 dispatch_after
 */
- (void)after {
    NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"asyncMain---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]);  // 打印当前线程
    });
}

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

 static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 只执行1次的代码(这里面默认是线程安全的)
    });

GCD 快速迭代方法:dispatch_apply

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    NSLog(@"apply---begin");
    dispatch_apply(6, queue, ^(size_t index) {
        NSLog(@"%zd---%@",index, [NSThread currentThread]);
    });
    NSLog(@"apply---end");

GCD 队列组:dispatch_group

NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
    NSLog(@"group---begin");
    
    dispatch_group_t group =  dispatch_group_create();
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务1
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        // 追加任务2
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        }
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        // 等前面的异步任务1、任务2都执行完毕后,回到主线程执行下边任务
        for (int i = 0; i < 2; ++i) {
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        }
        NSLog(@"group---end");
    });

GCD队列组监听:dispatch_group_notify

    /*
     * 业务异步串行执行,用GCD实现的四种方式
     * 1、dispatch_group_notify(group, queue, ^{ });
     * 2、dispatch_group_async(group, backQueue, ^{ });
     * 3、dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 会有线程等待阻塞当前线程
     * 4、信号量 = 0  实现;会有线程等待阻塞当前线程
     */
    
    // 被监听的group
    dispatch_group_t group = dispatch_group_create();
    // 将要执行后续block代码的队列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    
    // ----------------- 监听group执行完毕 后,在queue队列执行block代码
    dispatch_group_notify(group, queue, ^{
        // 任务代码
    });
    
    
    // ---------------- 全局并发队列
    dispatch_queue_t backQueue = dispatch_get_global_queue(0, 0);
    // 向group中添加 在某个队列中执行的任务
    dispatch_group_async(group, backQueue, ^{
        // 追加的任务
    });

    /*
     * 监听group执行完毕 之后在某个队列中执行某个任务
     * 跟 向group中追加某个队列中执行的任务 也有类似作用,但是group不会有线程等待,只是会造成先后执行的效果,但是不会有线程等待
     */

    // ------------ 在此处等待group中的任务执行完毕 然后 执行wait后面的代码,线程等待会阻塞当前线程
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    
    
    // 创建信号量 初始信号量0 线程等待
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    // 主队列中执行异步任务
    dispatch_async(queue, ^{
        // 信号量+1
        dispatch_semaphore_signal(semaphore);
    });
    // 信号量不为0时候-1,信号量=0时候线程等待
    // 异步任务异步执行后 直接带了wait,信号量=0 ,线程等待,等异步任务完毕时 信号量+1 不为零,此时线程不再等待,开始执行下面代码,会阻塞当前线程;
    dispatch_semaphore_wait(semaphore, 1);
    
    /*
     * semaphore = 0 时候 dispatch_semaphore_wait 等待信号量!=0,
     * 跟 dispatch_group_wait 等待group执行完毕 有一点相似。
     */

GCD中任务增删:dispatch_group_enter、dispatch_group_leave

// 向任务组添加任务
dispatch_group_enter(group);
// 将要添加的任务
dispatch_async(queue, ^{
    // 追加任务1
    [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
    NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
    // 成对出现 任务组任务执行完毕 撤销任务
    dispatch_group_leave(group);
});
// 等待group五秒钟
dispatch_group_wait(group, dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)));
// 要么五秒内执行完毕走到这里;要么五秒后超时,group还未执行完 就走到了这里
NSLog(@"等待之后");
    
// 如果只有enter 没有leave那么线程会一直存在 等待group执行完毕

GCD信号量:dispatch_semaphore_t


-(void)semaphoreLockTest{
    
    dispatch_semaphore_t semaphoreLock = dispatch_semaphore_create(1);
    
    // 创建两个购票的串行队列
    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_SERIAL);
    
    __weak typeof(&*self)weakSelf = self;
    // 第一个购票队列
    dispatch_async(queue1, ^{
        [weakSelf semaphoreGetTicket:semaphoreLock];
    });
    // 第二个购票队列
    dispatch_async(queue2, ^{
        [weakSelf semaphoreGetTicket:semaphoreLock];
    });

}

-(void)semaphoreGetTicket:(dispatch_semaphore_t)semaphore{
    while (1) {
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        if (YES) {
            // 有票
            // 模拟购票延时
            [NSThread sleepForTimeInterval:0.2];
            // 相当于解锁
            dispatch_semaphore_signal(semaphore);
        }else{
            // 无票
            // 相当于解锁
            dispatch_semaphore_signal(semaphore);
            break;
        }
        
    }
}

线程锁

iOS多线程-各种线程锁的简单介绍


本文参考文章:

相关文章

网友评论

      本文标题:OC 多线程之 GCD知识从基础到进阶 (1)

      本文链接:https://www.haomeiwen.com/subject/prifvftx.html