zoukankan      html  css  js  c++  java
  • iOS开发之GCD使用

    一、队列

    1、串行队列(Serial Dispatch Queue)

    // 串行队列的创建方法
    dispatch_queue_t queue = dispatch_queue_create("标识符", DISPATCH_QUEUE_SERIAL);

    2、并发队列(Concurrent Dispatch Queue)

    // 并发队列的创建方法 
    dispatch_queue_t queue = dispatch_queue_create("标识符", DISPATCH_QUEUE_CONCURRENT);

    3、系统主队列(Main Dispatch Queue)

    // 主队列的获取方法
    dispatch_queue_t queue = dispatch_get_main_queue();
    //相当于同步串行队列
    dispatch_queue_t queue = dispatch_queue_create("标识符", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ });

    4、全局并发队列(Global Dispatch Queue)

    // 全局并发队列的获取方法
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    //相当于异步并行队列
    dispatch_queue_t queue = dispatch_queue_create("标识符", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ });

     二、任务

    GCD 提供了同步执行任务的创建方法 dispatch_sync 和异步执行任务创建方法 dispatch_async

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

    队列和任务可以随意组合,达到不同目的。注意: 『主线程』 中调用 『主队列』+『同步执行』 会导致死锁问题。这是因为 主队列中追加的同步任务主线程本身的任务 两者之间相互等待,阻塞了 『主队列』,最终造成了主队列所在的线程(主线程)死锁问题。

    三、GCD 线程间的通信

    //线程间通信 (开启子线程处理完耗时操作,回到主线程刷新UI)
    - (void)communication {
        // 获取全局并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        // 获取主队列
        dispatch_queue_t mainQueue = dispatch_get_main_queue();
        
        dispatch_async(queue, ^{
            // 异步追加任务 1
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
            
            // 回到主线程
            dispatch_async(mainQueue, ^{
                // 追加在主线程中执行的任务
                [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
                NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
            });
        });
    }
    

    四、GCD 栅栏方法 (dispatch_barrier_async/dispatch_barrier_sync)

    dispatch_barrier_async就是将异步执行分成两组操作,而且第一组操作执行完之后,才能开始执行第二组操作。

    - (void)gcdBarrier
    {
       // 创建队列
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
     
        dispatch_async(queue, ^{
            NSLog(@"----1-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"----2-----%@", [NSThread currentThread]);
        });
     
        dispatch_barrier_async(queue, ^{
            NSLog(@"----barrier-----%@", [NSThread currentThread]);
        });
     
        dispatch_async(queue, ^{
            NSLog(@"----3-----%@", [NSThread currentThread]);
        });
        dispatch_async(queue, ^{
            NSLog(@"----4-----%@", [NSThread currentThread]);
        });
     
        // 可以看出在执行完栅栏前面的操作之后,才执行栅栏操作,最后再执行栅栏后边的操作
     
    }//珊栏方法的
    

    注意:1. dispatch_barrier_async , 会将添加到queue前面的任务执行完之后,才会执行后面的任务,并不会阻塞当前的线程

             2.dispatch_barrier_sync , 会将添加到queue前面的任务执行完之后,才会执行后面的任务,并且会阻塞当前的线程

    五、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

    dispatch_once能保证任务只会被执行一次,即使同时多线程调用也是线程安全的。常用于创建单例、swizzeld method等功能。

        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            //创建单例、method swizzled或其他任务
        });
    

    七、GCD 快速迭代方法:dispatch_apply

    void dispatch_apply(size_t iterations, dispatch_queue_t queue,
            DISPATCH_NOESCAPE void (^block)(size_t));
    该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等待全部处理执行结束,好处是可以重复执行某项操作并复用我们的Block了!
    第一个参数为重复次数; 第二个参数为追加对象的Dispatch Queue; 第三个参数为追加的操作,追加的Block中带有参数,这是为了按第一个参数重复追加Block并区分各个Block而使用。
    dispatch_apply函数是dispatch_sync函数和Dispatch Group的关联API。这是因为该函数会等待这些操作执行完毕再返回,内部操作执行是否同步依赖于传入的queue,外部就必定是同步的。
    - (void)gcdDispatchApply
    {
        //获取一个全局队列
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        //dispatch_queue_t queue = dispatch_queue_create("com.gcd.dispatchApply.serialQueue", NULL);
        //在全局队列queue上执行十次输出操作
        dispatch_apply(10, queue, ^(size_t index) {
            NSLog(@"%zu", index);
        });
        
        NSLog(@"done!");
    }
    

    注意:当传入的queue是并行队列时,重复执行的操作输出是无序的,done输出始终在最后。

             当传入的queue是串行队列时,重复执行的操作输出是有序的,done输出始终在最后。

    八、GCD 队列组:dispatch_group

    有时候我们会有这样的需求:分别异步执行2个耗时任务,然后当2个耗时任务都执行完毕后再回到主线程执行任务。这时候我们可以用到 GCD 的队列组。

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

    1、dispatch_group_notify

    监听 group 中任务的完成状态,当所有的任务都执行完成后,追加任务到 group 中,并执行任务。

    // 队列组 dispatch_group_notify
    - (void)groupNotify {
        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
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        });
        
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // 追加任务 2
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        });
        
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            // 等前面的异步任务 1、任务 2 都执行完毕后,回到主线程执行下边任务
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
    
            NSLog(@"group---end");
        });
    }
    //当前面两个任务都执行完成之后,才执行 dispatch_group_notify 相关 block 中的任务。

     2、dispatch_group_wait

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

    // 队列组 dispatch_group_wait
    - (void)groupWait {
        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
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
        });
        
        dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            // 追加任务 2
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
        });
        
        // 等待上面的任务全部完成后,会往下继续执行(会阻塞当前线程)
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
        
        NSLog(@"group---end");
    }
    //当前面两个任务执行完成之后,才执行 dispatch_group_wait 之后的操作。但是,使用dispatch_group_wait 会阻塞当前线程。

     3、dispatch_group_enter、dispatch_group_leave

    dispatch_group_enter 标志着一个任务追加到 group,执行一次,相当于 group 中未执行完毕任务数 +1

    dispatch_group_leave 标志着一个任务离开了 group,执行一次,相当于 group 中未执行完毕任务数 -1。

    当 group 中未执行完毕任务数为0的时候,才会使 dispatch_group_wait 解除阻塞,以及执行追加到 dispatch_group_notify 中的任务。

    /**
     * 队列组 dispatch_group_enter、dispatch_group_leave
     */
    - (void)groupEnterAndLeave {
        NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
        NSLog(@"group---begin");
        
        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, ^{
            // 追加任务 1
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
    
            dispatch_group_leave(group);
        });
        
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            // 追加任务 2
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"2---%@",[NSThread currentThread]);      // 打印当前线程
            
            dispatch_group_leave(group);
        });
        
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            // 等前面的异步操作都执行完毕后,回到主线程.
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"3---%@",[NSThread currentThread]);      // 打印当前线程
        
            NSLog(@"group---end");
        });
    }
    //当所有任务执行完成之后,才执行 dispatch_group_notify 中的任务。这里的dispatch_group_enterdispatch_group_leave 组合,其实等同于dispatch_group_async。

     九、GCD 信号量:dispatch_semaphore

    GCD 中的信号量是指 Dispatch Semaphore,是持有计数的信号。类似于过高速路收费站的栏杆。可以通过时,打开栏杆,不可以通过时,关闭栏杆。在 Dispatch Semaphore 中,使用计数来完成这个功能,计数小于 0 时等待,不可通过。计数为 0 或大于 0 时,计数减 1 且不等待,可通过。

    Dispatch Semaphore 提供了三个方法:

    1.dispatch_semaphore_create:创建一个 Semaphore 并初始化信号的总量

    2.dispatch_semaphore_signal:发送一个信号,让信号总量加 1

    3.dispatch_semaphore_wait:可以使总信号量减 1,信号总量小于 0 时就会一直等待(阻塞所在线程),否则就可以正常执行。

    Dispatch Semaphore 在实际开发中主要用于:

    1.保持线程同步,将异步执行任务转换为同步执行任务

    2.保证线程安全,为线程加锁

    1、Dispatch Semaphore 线程同步

    我们在开发中,会遇到这样的需求:异步执行耗时任务,并使用异步执行的结果进行一些额外的操作。换句话说,相当于,将将异步执行任务转换为同步执行任务。
    /**
     * semaphore 线程同步
     */
    - (void)semaphoreSync {
        
        NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
        NSLog(@"semaphore---begin");
        
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);//1.创建一个 Semaphore 并初始化信号的总量
        
        __block int number = 0;
        dispatch_async(queue, ^{
            // 追加任务 1
            [NSThread sleepForTimeInterval:2];              // 模拟耗时操作
            NSLog(@"1---%@",[NSThread currentThread]);      // 打印当前线程
            
            number = 100;
            
            dispatch_semaphore_signal(semaphore);//3.发送一个信号,让信号总量加 1
        });
        
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//2.可以使总信号量减 1,信号总量小于 0 时就会一直等待(阻塞所在线程),否则就可以正常执行。
        NSLog(@"semaphore---end,number = %zd",number);
    }
    //这是一个异步线程,没加信号量的话,应该是number = 0;加上信号量 number = 100;
    它的执行顺如下:

    1.semaphore 初始创建时计数为 0。

    2.异步执行任务 1 追加到队列之后,不做等待,接着执行 dispatch_semaphore_wait 方法,semaphore 减 1,此时 semaphore == -1,当前线程进入等待状态。

    3.然后,异步任务 1 开始执行。任务 1 执行到 dispatch_semaphore_signal 之后,总信号量加 1,此时 semaphore == 0,正在被阻塞的线程(主线程)恢复继续执行。

    4.最后打印 semaphore---end,number = 100

    这样就实现了线程同步,将异步执行任务转换为同步执行任务。

    2、 线程安全(使用 semaphore 加锁)

    /**
     * 线程安全:使用 semaphore 加锁
     * 初始化火车票数量、卖票窗口(线程安全)、并开始卖票
     */
    - (void)initTicketStatusSave {
        NSLog(@"currentThread---%@",[NSThread currentThread]);  // 打印当前线程
        NSLog(@"semaphore---begin");
        
        semaphoreLock = dispatch_semaphore_create(1);
        
        self.ticketSurplusCount = 50;
        
        // queue1 代表北京火车票售卖窗口
        dispatch_queue_t queue1 = dispatch_queue_create("net.bujige.testQueue1", DISPATCH_QUEUE_SERIAL);
        // queue2 代表上海火车票售卖窗口
        dispatch_queue_t queue2 = dispatch_queue_create("net.bujige.testQueue2", DISPATCH_QUEUE_SERIAL);
        
        __weak typeof(self) weakSelf = self;
        dispatch_async(queue1, ^{
            [weakSelf saleTicketSafe];
        });
        
        dispatch_async(queue2, ^{
            [weakSelf saleTicketSafe];
        });
    }
    
    /**
     * 售卖火车票(线程安全)
     */
    - (void)saleTicketSafe {
        while (1) {
            // 相当于加锁
            dispatch_semaphore_wait(semaphoreLock, DISPATCH_TIME_FOREVER);
            
            if (self.ticketSurplusCount > 0) {  // 如果还有票,继续售卖
                self.ticketSurplusCount--;
                NSLog(@"%@", [NSString stringWithFormat:@"剩余票数:%d 窗口:%@", self.ticketSurplusCount, [NSThread currentThread]]);
                [NSThread sleepForTimeInterval:0.2];
            } else { // 如果已卖完,关闭售票窗口
                NSLog(@"所有火车票均已售完");
                
                // 相当于解锁
                dispatch_semaphore_signal(semaphoreLock);
                break;
            }
            
            // 相当于解锁
            dispatch_semaphore_signal(semaphoreLock);
        }
    }
    //在考虑了线程安全的情况下,使用 dispatch_semaphore机制之后,得到的票数是正确的,没有出现混乱的情况。我们也就解决了多个线程同步的问题。

     十、GCD 定时器:dispatch_source

    用UIButton的类别实现一个注册倒计时的功能

    - (void)countDownFromTime:(NSInteger)startTime title:(NSString *)title unitTitle:(NSString *)unitTitle mainColor:(UIColor *)mColor countColor:(UIColor *)color {
        
        __weak typeof(self) weakSelf = self;
      
        // 剩余的时间
        __block NSInteger remainTime = startTime;
        
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        
        dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        // 每秒执行一次
        dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
        
        // 子线程(queue)执行event_handler
        dispatch_source_set_event_handler(timer, ^{
            
            if (remainTime <= 0) { // 倒计时结束
                dispatch_source_cancel(timer);
                // 主线程更新UI
                dispatch_async(dispatch_get_main_queue(), ^{
                    weakSelf.backgroundColor = mColor;
                    [weakSelf setTitle:title forState:UIControlStateNormal];
                    weakSelf.enabled = YES;
                });
            } else {
                NSString *timeStr = [NSString stringWithFormat:@"%ld", remainTime];
                dispatch_async(dispatch_get_main_queue(), ^{
                    weakSelf.backgroundColor = color;
                    [weakSelf setTitle:[NSString stringWithFormat:@"%@%@",timeStr,unitTitle] forState:UIControlStateDisabled];
                    weakSelf.enabled = NO;
                });
                remainTime--;
            }
        });
        dispatch_resume(timer);
        
    }
    

     

  • 相关阅读:
    记一次git fatal: Unable to find remote helper for 'https'问题的解决
    LambdaMART简介——基于Ranklib源码(二 Regression Tree训练)
    LambdaMART简介——基于Ranklib源码(一 lambda计算)
    用中文把玩Google开源的Deep-Learning项目word2vec
    Ubuntu18.04 一次性升级Python所有库
    CSAPP家庭作业(第二章)
    两个有序链表序列的合并
    sublime text 3 配置Python开发环境
    Java课程设计-泡泡堂(个人)
    二叉树的先序建立与遍历
  • 原文地址:https://www.cnblogs.com/laolitou-ping/p/14358428.html
Copyright © 2011-2022 走看看