美文网首页
iOS多线程实现——GCD的基本使用

iOS多线程实现——GCD的基本使用

作者: 听海听心 | 来源:发表于2017-12-11 16:31 被阅读7次

    今天和大家一起来讨论一下GCD的基本使用,有疏忽的地方,还望各位不吝赐教。


    一、多线程简介

    1、多线程相关的概念

    进程:

    1、进程是指在系统中正在运行的一个应用程序;
    2、每个进程是独立的,每个进程运行在专用且受保护的内存空间中。

    线程:

    1、一个进程执行任务,必须依赖线程(一个进程至少有一条线程);
    2、进程的所有任务都在线程中执行。

    线程的串行:

    1、一个线程中任务的执行是串行的
    2、如果要在一个线程中执行多个任务,只能一个一个按顺序执行,即同一时间一个线程只能执行一个任务;

    线程和进程的比较:

    1、线程是CPU调用(执行任务)的最小单位。
    2、进程是CPU分配资源的最小单位。
    3、一个程序可以对应多个进程,一个进程中可以有多个线程,但至少有一个。
    4、同一个进程中的线程共享进程的资源。

    多线程:

    一个进程中可以开启多条线程的,每一条线程可以并行(同时)执行不同的任务。其实在同一时间,CPU只能处理一条线程,多线程的并发(同时)执行,其实是一种假象,只不过是快速的在多条线程之间进行调度,也就是切换罢了。但是一般不建议开很多条线程,因为开线程越多,相应的开销就越大,对于CPU来说负担就越重。

    2、多线程的优缺点

    优点:
    1、能适当的提高程序的执行效率;
    2、能适当的提高资源的利用率(包括CPU和内存)。
    缺点:
    1、创建线程是有开销的,iOS 9下主要成本包括:内核数据结构(大约1KB)、栈空间(子线程512KB,主线程1MB,也可以使用-setStackSize:进行设置,但是必须是4KB的倍数,最小16KB),创建线程大约需要90毫秒的时间进行创建。
    2、如果开启大量的线程,会降低程序的性能。
    3、线程越多,CPU在线程上的开销就越大。
    4、程序设计更加复杂,包括在线程之间的通信和多线程之间的数据共享。

    3、多线程在iOS中的应用

    主线程

    一个iOS程序运行后,默认会开启一条线程,称为主线程或者UI线程。

    主线程的主要作用

    1、显示\刷新UI界面;
    2、处理UI事件(比如点击事件,滚动事件和拖拽事件等);
    3、注意不要将耗时操作放到主线程汇总;
    4、耗时操作会卡住主线程,严重影响UI的流畅度,造成不好的用户体验;
    5、在开发过程中,注意将耗时操作放到子线程中进行执行。

    二、多线程安全

    当多个线程同时访问一块资源的时候,会发生数据错乱和数据安全的问题。
    解决方案:互斥锁
    当线程A访问资源的时候加锁,保证其他线程无法在线程A访问期间进行对本资源的操作,在线程A操作完成后,会打开添加的互斥锁,之后其他线程可以按照同样的方式进行访问资源。

        格式:
        @synchronized(锁对象) {需要锁定的代码}
        使用方式:
        @synchronized(self) {}
    

    注意点:
    1、锁必须是全局唯一的,所以一般使用控制器作为标识。
    2、注意加锁的前提条件,在多线程访问同一块资源的时候再添加。
    3、加锁是需要耗费性能的。
    4、加锁的结果:线程同步(多条线程在同一条线上执行,按照顺序进行执行)
    关于原子属性和非原子属性的解释:
    *atomic:
    原子属性,会为setter方法加锁,默认为atomic。
    因为会为setter方法加锁,所以在使用的时候是线程安全的,但是会消耗大量的资源。
    *nonatomic:
    非原子属性,不会为setter方法加锁。
    虽然nonatomic是不安全的,但是出现多个线程同时访问一块资源的情况很少,为了保证效率,所以一般使用nonatomic。
    建议:
    尽量把这些争夺资源的任务交给服务器端处理,减小移动端的压力。

    三、iOS多线程实现方案

    技术方案 简介 语言 线程生命周期 使用频率
    pthread 一套通用的多线程API
    可移植,适用于多个平台
    使用难度大
    C 程序员管理 几乎不用
    NSThread 使用更加面向对象
    简单易用,可以直接操作线程对象
    OC 程序员管理 偶尔使用
    GCD 为了替代NSThread等的线程技术
    充分利用设备的多核
    C 自动管理 经常使用
    NSOperation 基于GCD
    相比GCD多了一些更简单实用的功能
    更加面向对象
    OC 自动管理 经常使用

    四、GCD多线程实现方案

    1、GCD简介

    全称是Grand Central Dispatch,是一个纯C语言的库,是苹果公司为多核的并行运算提出的解决方案。

    2、GCD优势

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

    3、任务和队列

    任务:执行的操作统称为任务
    队列:用来存放任务,并且调度任务在那些线程中执行

    4、使用步骤

    1、定制任务
    确定想做的任务
    2、将任务添加到队列中
    GCD会自动将队列中的任务取出来,放到对应的线程中执行
    任务的取出遵循队列的FIFO的原则:先进先出,后进后出

    5、执行任务的方式

    GCD中有两个用来执行任务的常用函数

    • 同步函数
      同步:只能在当前线程中执行任务,不具备开启新线程的能力
    • 异步函数
      异步:可以在新的线程中执行任务,具备开启新线程的能力

    6、队列的类型

    GCD中队列分为两大类型

    • 并发队列(Concurrent Dispatch Queue)
      可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
    • 串行队列 (Serial Dispatch Queue)
      可以让任务一个接着一个的执行(一个执行完之后,再执行下一个任务)

    7、术语的相关解释

    • 同步和异步主要影响的是能不能开启新的线程 这里指的是函数
      同步:只能在当前线程中执行任务,不具备开启新线程的能力
      异步:可以在新的线程中执行任务,具备开启新线程的能力
    • 并发和串行主要影响:任务的执行方式 这里指的是队列
      并发:可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
      串行:可以让任务一个接着一个的执行(一个执行完之后,再执行下一个任务)

    五、pthread简单使用

    因为pthread用的不多,所以简单提一下,关于其创建线程的使用如下:

       // 1、创建线程对象
        pthread_t thread;
        
        // 2、创建线程
        /*
         * 第一个参数:线程对象,传递地址
         * 第二个参数:线程的属性 NULL
         * 第三个参数:指向函数的指针
         * 第四个参数:函数需要接受的参数
         */
        pthread_create(&thread, NULL, task, NULL);
        // 3、task实现
        void *task(void *param){
    
            NSLog(@"%@",[NSThread currentThread]);
            return NULL;
        }
    

    六、GCD简单使用

    创建并发队列的方式

    1、create创建并发队列(主动创建)

        /*
         * 第一个参数:c语言的字符串,标签
         * 第二个参数:队列的类型
         * DISPATCH_QUEUE_CONCURRENT 并发
         * DISPATCH_QUEUE_SERIAL 串行
         */
        
        dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd",DISPATCH_QUEUE_CONCURRENT);
        
    

    2、获取全局并发队列(系统中本身存在,拿过来用就行了)

      /* 和DISPATCH_QUEUE_CONCURRENT创建的并发队列 是等价的
       * 第一个参数:优先级 四种 一般用默认的即可
            #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即可。
       */
      
      dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
      
    

    创建串行队列的方式

    1、create创建串行队列(主动创建)

        /*
         * 第一个参数:c语言的字符串,标签
         * 第二个参数:队列的类型
         * DISPATCH_QUEUE_CONCURRENT 并发
         * DISPATCH_QUEUE_SERIAL 串行 或者传递NULL 
         */
        
        dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd",DISPATCH_QUEUE_SERIAL);
        
    

    2、使用主队列(跟主线程相关联的队列)

        /*
       * 主队列是GCD自带的一种特殊的串行队列,放在主队列中的任务都会放在主线程中进行执行。
       * 第一个参数:c语言的字符串,标签
       * 第二个参数:队列的类型
       * DISPATCH_QUEUE_CONCURRENT 并发
       * DISPATCH_QUEUE_SERIAL 串行 或者传递NULL 
       */
      
    

    函数和队列的组合情况

    1、异步函数 + 并发队列: 会开启多条线程,队列中的任务是异步(并发)执行

        // 1.创建队列
        /*
         * 第一个参数:c语言的字符串,标签
         * 第二个参数:队列的类型
         * DISPATCH_QUEUE_CONCURRENT 并发
         * DISPATCH_QUEUE_SERIAL 串行
         */
        
        dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd", DISPATCH_QUEUE_CONCURRENT);
        
        // 2、封装任务 > 将任务添加到队列中
        /* 
         * 第一个参数:要执行的队列
         * 第二个参数:要执行的任务
         * 注意:
            并不是有几个任务就开几条线程,具体开几条线程由系统进行衡量!
         */
        
        dispatch_async(queue, ^{
            NSLog(@"1---------%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"2---------%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"3---------%@",[NSThread currentThread]);
        });
        // 打印结果:
            3---------<NSThread: 0x60800007e880>{number = 5, name = (null)}
            1---------<NSThread: 0x600000078700>{number = 3, name = (null)}
            2---------<NSThread: 0x60800007e2c0>{number = 4, name = (null)}
    

    2、异步函数 + 串行队列: 会开启一条线程,队列中的任务是同步(串行)执行的

        // 1.创建队列
        /*
         * 第一个参数:c语言的字符串,标签
         * 第二个参数:队列的类型
         * DISPATCH_QUEUE_CONCURRENT 并发
         * DISPATCH_QUEUE_SERIAL 串行
         */
        
        dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd", DISPATCH_QUEUE_SERIAL);
        
        // 2、封装任务 > 将任务添加到队列中
        /*
         * 第一个参数:要执行的队列
         * 第二个参数:要执行的任务
         */
        
        dispatch_async(queue, ^{
            NSLog(@"1---------%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"2---------%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"3---------%@",[NSThread currentThread]);
        });
        // 打印结果:
            1---------<NSThread: 0x60000007d180>{number = 3, name = (null)}
            2---------<NSThread: 0x60000007d180>{number = 3, name = (null)}
            3---------<NSThread: 0x60000007d180>{number = 3, name = (null)}
    

    3、同步函数 + 并发队列:不会开线程,任务是串行执行的

        // 1.创建队列
        /*
         * 第一个参数:c语言的字符串,标签
         * 第二个参数:队列的类型
         * DISPATCH_QUEUE_CONCURRENT 并发
         * DISPATCH_QUEUE_SERIAL 串行
         */
        
        dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd", DISPATCH_QUEUE_CONCURRENT);
       
        // 2、封装任务 > 将任务添加到队列中
        /*
         * 第一个参数:要执行的队列
         * 第二个参数:要执行的任务
         */
        
        dispatch_sync(queue, ^{
            NSLog(@"1---------%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"2---------%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"3---------%@",[NSThread currentThread]);
        });
        // 打印结果:
            1---------<NSThread: 0x60000007e800>{number = 1, name = main}
            2---------<NSThread: 0x60000007e800>{number = 1, name = main}
            3---------<NSThread: 0x60000007e800>{number = 1, name = main}
    

    4、同步函数 + 串行队列:不会开线程,任务是串行执行的(效果和3同)

        // 1.创建队列
        /*
         * 第一个参数:c语言的字符串,标签
         * 第二个参数:队列的类型
         * DISPATCH_QUEUE_CONCURRENT 并发
         * DISPATCH_QUEUE_SERIAL 串行
         */
        
        dispatch_queue_t queue = dispatch_queue_create("com.jianshu.gcd",DISPATCH_QUEUE_SERIAL);
        
        // 2、封装任务 > 将任务添加到队列中
        /*
         * 第一个参数:要执行的队列
         * 第二个参数:要执行的任务
         */
        
        dispatch_sync(queue, ^{
            NSLog(@"1---------%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"2---------%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"3---------%@",[NSThread currentThread]);
        });
        // 打印结果:
            1---------<NSThread: 0x60000007e800>{number = 1, name = main}
            2---------<NSThread: 0x60000007e800>{number = 1, name = main}
            3---------<NSThread: 0x60000007e800>{number = 1, name = main}
    

    5、异步函数 + 主队列:不会开线程,任务是串行执行的

        // 1.获得主队列
    
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        // 2、封装任务 > 将任务添加到队列中
        /*
         * 第一个参数:要执行的队列
         * 第二个参数:要执行的任务
         */
        
        dispatch_async(queue, ^{
            NSLog(@"1---------%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"2---------%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"3---------%@",[NSThread currentThread]);
        });
        // 打印结果:
            1---------<NSThread: 0x60000007e800>{number = 1, name = main}
            2---------<NSThread: 0x60000007e800>{number = 1, name = main}
            3---------<NSThread: 0x60000007e800>{number = 1, name = main}
    

    6、同步函数(立刻马上执行) + 主队列:死锁

        // 1.获得主队列
    
        dispatch_queue_t queue = dispatch_get_main_queue();
        
        // 2、封装任务 > 将任务添加到队列中
        /*
         * 第一个参数:要执行的队列
         * 第二个参数:要执行的任务
         */
        
        dispatch_sync(queue, ^{
            NSLog(@"1---------%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"2---------%@",[NSThread currentThread]);
        });
        
        dispatch_sync(queue, ^{
            NSLog(@"3---------%@",[NSThread currentThread]);
        });
        
        // 说明:
            主队列的特点:如果主队列发现当前主线程正在执行任务,会等到主线程执行完当前任务后,再调度主队列中的任务,如果在子线程中使用以下方法就可以避免死锁
            同步函数:立刻马上执行,我不执行完,后面的谁都别想执行
            异步函数:如果我不执行完,后面的也可以执行
    

    各种队列执行总结

    并发队列 手动创建串行队列 主队列
    同步 没有开启新线程
    串行执行任务
    没有开启新线程
    串行执行任务
    没有开启新线程
    串行执行任务
    异步 开启新线程
    并发执行任务
    开启新线程
    串行执行任务
    没有开启新线程
    串行执行任务

    七、GCD线程之间的通信

    // 1、获取全局并发队列
    
     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
    
    // 2、封装任务 > 将任务添加到队列中
    
        dispatch_async(queue, ^{
            // 在这里执行子线程中相关耗时的操作
            /*
                 例如网络请求或者下载任务
              */
            dispatch_async(dispatch_get_main_queue(), ^{
                
                // 在这里进行和主线程的通信,主要是刷新UI的相关操作,这里用同步函数也是可以的,并不会造成死锁。
                
            });
            
        });
    

    八、GCD常用函数

    1、延迟执行

        1)延迟执行的第一种方法
        [self performSelector:@selector(task) withObject:nil afterDelay:2.0];
        
        2)延迟执行的第二种方法
         [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(task) userInfo:nil repeats:YES];
    
        3)延迟执行的第二种方法 
        /* 主要可以控制延迟执行的操作在哪个队列中执行 dispatch_get_main_queue() 修改此参数即可
         * 第一个参数:DISPATCH_TIME_NOW 从现在开始计算时间
         * 第二个参数:延迟的时间 GCD的时间单位 纳秒
         * 第三个参数:dispatch_get_main_queue 主队列
         */
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            
            NSLog(@"delay----%@",[NSThread currentThread]);
            
        });
    

    2、一次性执行
    使用场景:单例模式(在以后的分享中会提到。。。。。。)

        // static 静态全局变量,作用域是整个应用程序的生命周期
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"once-----");
        });
         注意:
                 一次性代码不能放在懒加载中 会出问题
                 因为此代码只执行一次,在第一次创建的时候会把对象返回,但是在第二次创建对象的时候,不会再执行一次性代码,所以对象返回值为空。
    

    3、GCD的栅栏函数
    控制并发队列中异步执行任务的执行顺序。

        // 获得全局并发队列
        // 栅栏函数不能使用全局并发队列
        //    dispatch_queue_t queue = dispatch_get_global_queue(0, 0); 
        dispatch_queue_t queue = dispatch_queue_create("en", DISPATCH_QUEUE_CONCURRENT);
        // 异步函数
        dispatch_async(queue, ^{
            NSLog(@"1--------%@",[NSThread currentThread]);
        });
        
        dispatch_async(queue, ^{
            NSLog(@"2--------%@",[NSThread currentThread]);
        });
        // 栅栏函数 先执行1、2,关于1、2的先后是不能确定的,再执行3。
        dispatch_barrier_async(queue, ^{
            NSLog(@"+++++++++++++++++++");
        });
        dispatch_async(queue, ^{
            NSLog(@"3--------%@",[NSThread currentThread]);
        });
          // 打印结果:
            1--------<NSThread: 0x6000000754c0>{number = 3, name = (null)}
            2--------<NSThread: 0x60000006a340>{number = 4, name = (null)}
            +++++++++++++++++++
            3--------<NSThread: 0x60000006a340>{number = 4, name = (null)}
    

    4、GCD快速迭代
    迭代其实就是遍历,说白了就是一个循环。

        // 1)快速迭代的第一种方法
            for循环,但是for循环是同步执行的。
    
        // 2)快速迭代的第二种方法
        /* 主线程和子线程一起完成任务 任务的执行是并发的
         * 第一个参数:迭代的次数
         * 第二个参数:并发队列 只能传并发队列,如果传主队列,死锁,如果传手动创建的串行队列跟for循环效果一样
         * 第三个参数:索引
         */
        dispatch_apply(10, dispatch_get_global_queue(0, 0), ^(size_t index) {
            
            NSLog(@"%@-----%zu",[NSThread currentThread],index);
            
        });
    

    5、GCD定时器

        /** 定时器 */
        @property (nonatomic, strong) dispatch_source_t timer;
        // 创建timer
        /* GCD中的定时器是绝对精准的,不会受到RunLoop的影响!
         * 第一个参数:类型 DISPATCH_SOURCE_TYPE_TIMER
         * 第二个参数:描述信息
         * 第三个参数:更详细的描述信息
         * 第四个参数:队列 决定GCD的timer在那个线程中执行
         */
        // 以局部变量的形式进行定义,timer不会执行的,因为在执行之前已经被释放了
        self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
        /*
         * 第一个参数:定时器对象
         * 第二个参数:DISPATCH_TIME_NOW 起始时间
         * 第三个参数:间隔时间
         * 第四个参数:精准度 允许误差,此处传0即可
         */
        dispatch_source_set_timer(self.timer, DISPATCH_TIME_NOW, 2.0 * NSEC_PER_SEC, 0.0 * NSEC_PER_SEC);
        //  设置定时器的任务
        dispatch_source_set_event_handler(self.timer, ^{
            
            NSLog(@"执行任务");
            
        });
        // 开启定时器
        dispatch_resume(self.timer);
    

    九、GCD队列组的使用

    跟栅栏函数相类似,也是用来控制并发队列中异步执行任务的执行顺序的。

        // 1、获得全局并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        // 2、创建队列组
        dispatch_group_t group = dispatch_group_create();
        // 3、异步函数
        /* 这个函数做了什么?
          1、封装任务 
          2、将任务添加到队列中
          3、会监听任务执行情况,通知group
        */
        dispatch_group_async(group, queue, ^{
            
            NSLog(@"1---------%@",[NSThread currentThread]);
        });
        
        dispatch_group_async(group, queue, ^{
            
            NSLog(@"2---------%@",[NSThread currentThread]);
        });
        
        dispatch_group_async(group, queue, ^{
            
            NSLog(@"3---------%@",[NSThread currentThread]);
        });
        // 4、拦截通知,当所有的队列组中的任务执行完毕以后会执行这个方法
        dispatch_group_notify(group, queue, ^{
            NSLog(@"end--------end");
        });
        // 打印结果:
            2---------<NSThread: 0x60800007a8c0>{number = 7, name = (null)}
            1---------<NSThread: 0x60800007a800>{number = 6, name = (null)}
            3---------<NSThread: 0x608000078980>{number = 8, name = (null)}
            end--------end
    
    

    另外一种GCD队列组的实现方式(以前使用的)

        // 获得全局并发队列
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        // 创建队列组
        dispatch_group_t group = dispatch_group_create();
        // 此方法之后执行的执行的异步任务会被纳入到队列组的监听范围内
        // 配对使用
        dispatch_group_enter(group);
        
        dispatch_group_async(group, queue, ^{
           NSLog(@"1---------%@",[NSThread currentThread]);
            
            // 离开群组
            dispatch_group_leave(group);
        });
        
        dispatch_group_enter(group);
        
        dispatch_group_async(group, queue, ^{
            NSLog(@"2---------%@",[NSThread currentThread]);
            
            // 离开群组
            dispatch_group_leave(group);
        });
        
        dispatch_group_enter(group);
        
        dispatch_group_async(group, queue, ^{
            NSLog(@"3---------%@",[NSThread currentThread]);
            
            // 离开群组
            dispatch_group_leave(group);
        });
        
        // 当所有的队列组中的任务执行完毕以后会执行这个方法
        // 内部本身是异步的,不会阻塞
        dispatch_group_notify(group, queue, ^{
            
            NSLog(@"end--------end");
        });
    
        // 其他的监听方法 DISPATCH_TIME_FOREVER 死等 等到所有任务都执行完毕才执行这个方法
        // 这里是阻塞的,如果下面方法没有执行,以后的方法不会执行的。
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    

    十、GCD异步函数的补充

    GCD异步函数有两种实现方式,具体的异同比较如下:

     1、block 实现
     // dispatch_async(<dispatch_queue_t  _Nonnull queue>, <^(void)block>)
     2、方法调用的方式实现
        /*
         * 第一个参数:队列
         * 第二个参数:参数
         * 第三个参数:要调用的函数名称
         */
        dispatch_async_f(dispatch_get_global_queue(0, 0), nil ,task);
        dispatch_async_f(dispatch_get_global_queue(0, 0), nil ,task);
        dispatch_async_f(dispatch_get_global_queue(0, 0), nil ,task);
    //  调用函数的实现
    void task(void *params){
        NSLog(@"%s--------%@",__func__,[NSThread currentThread]);
    }
    

    写在最后的话:关于GCD的知识今天就分享到这里,关于iOS多线程实现方面的问题欢迎大家和我交流,共同进步,谢谢各位。

    相关文章

      网友评论

          本文标题:iOS多线程实现——GCD的基本使用

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