美文网首页
iOS多线程-GCD的使用

iOS多线程-GCD的使用

作者: KMingMing | 来源:发表于2020-05-22 20:58 被阅读0次

    1.多线程相关的几个概念

    1.1 任务

    任务:就是执行操作的意思,换句话说就是你在线程中执行的那段代码。在GCD中是放在block中的。执行任务有两种方式:同步执行和异步执行。两者的主要区别是是否具备开启新线程的能力。

    • 同步:不具备开启新线程的能力,只能在当前线程中执行任务。
    • 异步:具备开启新线程的能力,可以在新的线程中执行任务。
      注意:异步任务具备开启新线程能力,仅仅表示有这个能力,但是不一定会开启新的线程,下面会详细说明各种情况。

    1.2 队列

    队列:这里的队列指任务队列,即用来存放任务的队列。队列是一种特殊的线性表,采用FIFO(先进先出)的原则,即新任务总是被插入到队列的末尾,而读取任务的时候总是从队列的头部开始读取。每读取一个任务,则从队列中释放一个任务。在GCD中有两种队列:串行队列并行队列

    • 串行队列:队列中的任务是一个接一个的执行,按照FIFO的原则,先添加的任务先执行
    • 并行队列:队列中的任务可以同时(并行)执行

    2.GCD的使用

    2.1 获取队列的几种方式

    1. 获取系统主线程中的主队列(串行队列)
        //获取主队列
        dispatch_queue_main_t mainQ = dispatch_get_main_queue();
    
    1. 获取系统提供的全局队列(并行队列)
        //获取全局并行队列
        dispatch_queue_global_t globalQ = dispatch_get_global_queue(0, 0);
    
    1. 创建串行队列
        //创建串行队列
        dispatch_queue_t serialQ = dispatch_queue_create("com.example.serial", DISPATCH_QUEUE_SERIAL);
    
    1. 创建并行队列
        //创建并行队列
        dispatch_queue_t concurrentQ = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
    

    2.2 任务创建的方式

    • 同步执行
        dispatch_queue_global_t queue = dispatch_get_global_queue(0, 0);
        dispatch_sync(queue, ^{
            // 执行任务的代码写这里
        });
    
    • 异步执行
        dispatch_queue_global_t queue = dispatch_get_global_queue(0, 0);
        dispatch_async(queue, ^{
            // 执行任务的代码写这里
        });
    

    2.3 任务和队列的组合方式

    并行队列 串行队列 主队列
    同步执行
    异步执行 可以开启多条新线程 只能开启一条新线程

    "否" 表示不能开启新线程

    代码论证

        //获取主队列
        dispatch_queue_main_t mainQ = dispatch_get_main_queue();
        //创建串行队列
        dispatch_queue_t serialQ = dispatch_queue_create("com.example.serial", DISPATCH_QUEUE_SERIAL);
        //创建并行队列
         dispatch_queue_t concurrentQ = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
    
    • 同步 + 并行
        dispatch_sync(concurrentQ, ^{
            NSLog(@"---------任务1--------%@",[NSThread currentThread]);
        });
        dispatch_sync(concurrentQ, ^{
            NSLog(@"---------任务2--------%@",[NSThread currentThread]);
        });
        dispatch_sync(concurrentQ, ^{
            NSLog(@"---------任务3--------%@",[NSThread currentThread]);
        });
        //    ---------任务1--------<NSThread: 0x600002b140c0>{number = 1, name = main}
        //    ---------任务2--------<NSThread: 0x600002b140c0>{number = 1, name = main}
        //    ---------任务3--------<NSThread: 0x600002b140c0>{number = 1, name = main}
    
    • 同步 + 串行
        dispatch_sync(serialQ, ^{
            NSLog(@"---------任务1--------%@",[NSThread currentThread]);
        });
        dispatch_sync(serialQ, ^{
            NSLog(@"---------任务2--------%@",[NSThread currentThread]);
        });
        dispatch_sync(serialQ, ^{
            NSLog(@"---------任务3--------%@",[NSThread currentThread]);
        });
        //    ---------任务1--------<NSThread: 0x600002b140c0>{number = 1, name = main}
        //    ---------任务2--------<NSThread: 0x600002b140c0>{number = 1, name = main}
        //    ---------任务3--------<NSThread: 0x600002b140c0>{number = 1, name = main}
    
    • 同步 + 主队列
        dispatch_async(serialQ, ^{
            dispatch_sync(mainQ, ^{
                 NSLog(@"---------任务1--------%@",[NSThread currentThread]);
             });
             dispatch_sync(mainQ, ^{
                 NSLog(@"---------任务2--------%@",[NSThread currentThread]);
             });
             dispatch_sync(mainQ, ^{
                 NSLog(@"---------任务3--------%@",[NSThread currentThread]);
             });
            //    ---------任务1--------<NSThread: 0x600002b140c0>{number = 1, name = main}
            //    ---------任务2--------<NSThread: 0x600002b140c0>{number = 1, name = main}
            //    ---------任务3--------<NSThread: 0x600002b140c0>{number = 1, name = main}
        });
    
    • 异步 + 并行
        dispatch_async(concurrentQ, ^{
            NSLog(@"---------任务1--------%@",[NSThread currentThread]);
        });
        dispatch_async(concurrentQ, ^{
            NSLog(@"---------任务2--------%@",[NSThread currentThread]);
        });
        dispatch_async(concurrentQ, ^{
            NSLog(@"---------任务3--------%@",[NSThread currentThread]);
        });
        //    ---------任务1--------<NSThread: 0x600000075100>{number = 7, name = (null)}
        //    ---------任务2--------<NSThread: 0x60000007a800>{number = 3, name = (null)}
        //    ---------任务3--------<NSThread: 0x600000001ac0>{number = 4, name = (null)}
        
    
    • 异步 + 串行
        dispatch_async(serialQ, ^{
            NSLog(@"---------任务1--------%@",[NSThread currentThread]);
        });
        dispatch_async(serialQ, ^{
            NSLog(@"---------任务2--------%@",[NSThread currentThread]);
        });
        dispatch_async(serialQ, ^{
            NSLog(@"---------任务3--------%@",[NSThread currentThread]);
        });
        //    ---------任务1--------<NSThread: 0x6000038f5740>{number = 5, name = (null)}
        //    ---------任务2--------<NSThread: 0x6000038f5740>{number = 5, name = (null)}
        //    ---------任务3--------<NSThread: 0x6000038f5740>{number = 5, name = (null)}
    
    • 异步 + 主队列
        dispatch_async(mainQ, ^{
            NSLog(@"---------任务1--------%@",[NSThread currentThread]);
        });
        dispatch_async(mainQ, ^{
            NSLog(@"---------任务2--------%@",[NSThread currentThread]);
        });
        dispatch_async(mainQ, ^{
            NSLog(@"---------任务3--------%@",[NSThread currentThread]);
        });
        //    ---------任务1--------<NSThread: 0x600001b08ec0>{number = 1, name = main}
        //    ---------任务2--------<NSThread: 0x600001b08ec0>{number = 1, name = main}
        //    ---------任务3--------<NSThread: 0x600001b08ec0>{number = 1, name = main}
    

    2.3 GCD的应用

    1. dispatch_after
      用于指定时间之后执行任务
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"3秒钟过后执行");
        });
    
    1. dispatch_barrier
      异步并行执行任务是无序的,有时候需要控制他们的执行顺序,可以使用
        dispatch_queue_t queue = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
        
        dispatch_async(queue, ^{
            NSLog(@"1-------%@",[NSThread currentThread]);
            for (int i=0; i<3; i++) {
                NSLog(@"1----❤️-----%d",i);
            }
        });
        dispatch_async(queue, ^{
            NSLog(@"2-------%@",[NSThread currentThread]);
            for (int i=0; i<3; i++) {
                NSLog(@"2----😁-----%d",i);
            }
        });
        
        dispatch_barrier_sync(queue, ^{
            NSLog(@"3-------%@",[NSThread currentThread]);
            for (int i=0; i<3; i++) {
                NSLog(@"3----你的心像一道墙-----%d",i);
            }
        });
        dispatch_async(queue, ^{
            NSLog(@"4-------%@",[NSThread currentThread]);
            for (int i=0; i<3; i++) {
                NSLog(@"4----🤖-----%d",i);
            }
        });
    
    1. dispatch_group_enter、dispatch_group_leave
      当有多个网络请求同时发送的时候,希望所有请求回调都结束再处理一些逻辑,可以使用 dispatch_group_enter、dispatch_group_leave
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_group_t group = dispatch_group_create();
        
        // 当有多个网络请求同时发送的时候,希望所有请求回调都结束再处理一些逻辑,可以使用 dispatch_group_enter、dispatch_group_leave
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i < 3; i++) {
                sleep(1);        //模拟异步耗时操作
                NSLog(@"async1 ------ %ld ----- %@",i,[NSThread currentThread]);
            }
            dispatch_group_leave(group);
        });
        
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i < 3; i++) {
                sleep(1);        //模拟异步耗时操作
                NSLog(@"async2 ------ %ld ----- %@",i,[NSThread currentThread]);
            }
            dispatch_group_leave(group);
        });
        
        dispatch_group_enter(group);
        dispatch_async(queue, ^{
            for (NSInteger i = 0; i < 3; i++) {
                sleep(1);        //模拟异步耗时操作
                NSLog(@"async3 ------ %ld ----- %@",i,[NSThread currentThread]);
            }
            dispatch_group_leave(group);
        });
        
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            NSLog(@"终于到我了 --- %@",[NSThread currentThread]);
        });
    
    1. dispatch_semaphore_t 信号量
      dispatch_semaphore_create(0) 创建一个信号量
      dispatch_semaphore_wait 信号量减1
      dispatch_semaphore_signal 信号量加1
        //创建信号量
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            for (int i=0; i<7; i++) {
                sleep(1.5);
                NSLog(@"----❤️-----%d",i);
            }
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            for (int i=0; i<7; i++) {
                sleep(1.5);
                NSLog(@"----🤩-----%d",i);
            }
            dispatch_semaphore_signal(semaphore);
        });
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            for (int i=0; i<7; i++) {
                sleep(1.5);
                NSLog(@"----🤖-----%d",i);
            }
        });
    
    1. gcd定时器
      注意:要用一个变量引用 timer ,否则会在方法执行完就释放了。
      @property (nonatomic, strong) dispatch_source_t timer;
        dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
        self.timer = timer;// @property (nonatomic, strong) dispatch_source_t timer; 
        // 第二个参数是开始时间,第四个参数是精确度,0表示没有误差
        dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
        dispatch_source_set_event_handler(timer, ^{
            NSLog(@"123123123");
        });
        
        dispatch_resume(timer);
    
    

    3.多线程常见面试题

    3.1 进程和线程的概念?

    ==进程==: 在iOS中,可以看做一个正在运行的程序,一个app只能有一个进程。
    ==线程==: 线程是进程的基本执行单元,作用是执行进程中的代码,一个进程最少有一个线程,叫做主线程。

    3.2 同步和异步的区别?

    同步不具备开启新线程的能力,异步具备开启新线程的能力。

    3.3 多线程的原理?

    单核CPU同一时刻只能处理1条线程,只有1条线程在工作(执行),多线程并发(同时)执行,其实是CPU快速地在多条线程之间调度(切换),如果CPU调度线程的时间足够快,就造成了多线程并发执行的假象

    3.4 什么情况下会造成死锁?

    在当前串行队列所在的线程中,同步的向当前串行队列添加一个新的任务,就会产生死锁。
    例如: 在主线程中,同步向主队列中添加一个任务就会造成死锁

    相关文章

      网友评论

          本文标题:iOS多线程-GCD的使用

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