美文网首页
多线程之GCD

多线程之GCD

作者: 云天涯丶 | 来源:发表于2018-04-17 14:48 被阅读7次

    GCD(Grand Central Dispatch) 介绍

    GCD属于系统级的线程管理,在Dispatch queue中执行需要执行的任务性能非常的高。GCD中的FIFO队列称为dispatch queue,用来保证先进来的任务先得到执行。

    1、基本概念

    1、执行任务的方式
    ①同步 sync

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

    ②异步 async

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

    2、队列

    系统级:
    ①dispatch_get_global_queue 全局队列,一个并行的队列
    ②dispatch_get_main_queue 主队列,主线程中的唯一队列,一个串行队列

    自定义:
    dispatch_queue_create(const char *_Nullable label,
    dispatch_queue_attr_t _Nullable attr)

    2、介绍

    1、dispatch_get_global_queue(long identifier, unsigned long flags)

    全局并发队列,系统级:dispatch_get_global_queue

    参数:
    identifier:优先级,只是CPU调用的级别不同,不保证前后顺序
    #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
    flags:保留值。填0

    注:

    DISPATCH_QUEUE_PRIORITY_HIGH映射到QOS_CLASS_USER_INITIATED类。
    DISPATCH_QUEUE_PRIORITY_DEFAULT映射到QOS_CLASS_DEFAULT类。
    DISPATCH_QUEUE_PRIORITY_LOW映射到QOS_CLASS_UTILITY类。
    DISPATCH_QUEUE_PRIORITY_BACKGROUND映射到QOS_CLASS_BACKGROUND类。
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    2、dispatch_queue_create(const char *_Nullable label,dispatch_queue_attr_t _Nullable attr)

    参数:
    label:const char * 类型 标签,可选,可为NULL
    attr:
    串行 DISPATCH_QUEUE_SERIAL或NULL(开辟一个子线程,任务在这个线程里顺序执行)
    并发 DISPATCH_QUEUE_CONCURRENT(开辟多个子线程,任务在各个线程里并发执行)

    dispatch_queue_t queue = dispatch_queue_create("xixi", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
      NSLog(@"%@",[NSThread currentThread]);
    });
    

    也可以给自定义队列设置优先级,有两种方式:dispatch_queue_attr_make_with_qos_class、dispatch_set_target_queue

    dispatch_queue_attr_make_with_qos_class

    attr:同上

    qos_class:

    QOS_CLASS_USER_INTERACTIVE:user interactive等级表示任务需要被立即执行提供好的体验,用来更新UI,响应事件等。这个等级最好保持小规模。
    QOS_CLASS_USER_INITIATED:user initiated等级表示任务由UI发起异步执行。适用场景是需要及时结果同时又可以继续交互的时候。
    QOS_CLASS_UTILITY:utility等级表示需要长时间运行的任务,伴有用户可见进度指示器。经常会用来做计算,I/O,网络,持续的数据填充等任务。这个任务节能。
    QOS_CLASS_BACKGROUND:background等级表示用户不会察觉的任务,使用它来处理预加载,或者不需要用户交互和对时间不敏感的任务。
    

    relative_priority:负偏移量 该值必须小于0且大于或等于QOS_MIN_RELATIVE_PRIORITY,否则此函数返回NULL(QOS_MIN_RELATIVE_PRIORITY是-15)

    dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_CONCURRENT, QOS_CLASS_DEFAULT, -1);
    dispatch_queue_t queue = dispatch_queue_create("xixi",attr);
    dispatch_async(queue, ^{
      NSLog(@"%@",[NSThread currentThread]);
    });
    
    dispatch_set_target_queue
    dispatch_queue_t queue = dispatch_queue_create("xixi",NULL); //需要设置优先级的queue
    dispatch_queue_t tQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0); //参考优先级
    dispatch_set_target_queue(queue, tQueue); //设置queue和tQueue的优先级一样
    dispatch_async(queue, ^{
        NSLog(@"%@",[NSThread currentThread]);
    });
    

    dispatch_set_target_queue:可以设置优先级,也可以设置队列层级体系,比如让多个串行和并行队列在统一一个串行队列里串行执行,如下

        dispatch_queue_t serialQueue = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t firstQueue = dispatch_queue_create("firstqueue", DISPATCH_QUEUE_SERIAL);
        dispatch_queue_t secondQueue = dispatch_queue_create("secondqueue", DISPATCH_QUEUE_CONCURRENT);
    
        dispatch_set_target_queue(firstQueue, serialQueue);
        dispatch_set_target_queue(secondQueue, serialQueue);
    
        dispatch_async(firstQueue, ^{
            NSLog(@"1");
            [NSThread sleepForTimeInterval:3.f];
        });
        dispatch_async(secondQueue, ^{
            NSLog(@"2");
            [NSThread sleepForTimeInterval:2.f];
        });
        dispatch_async(secondQueue, ^{
            NSLog(@"3");
            [NSThread sleepForTimeInterval:1.f];
        });
    
    3、dispatch_after

    延时提交block,不会阻塞线程
    参数:
    dispatch_time_t when:dispatch_time(dispatch_time_t when, int64_t delta)
    when有两个值:
    DISPATCH_TIME_NOW 当前
    DISPATCH_TIME_FOREVER 遥远的未来

    delta:纳秒
    #define NSEC_PER_SEC 1000000000ull //每秒有多少纳秒
    #define USEC_PER_SEC 1000000ull //每秒有多少毫秒
    #define NSEC_PER_USEC 1000ull //每毫秒有多少纳秒

    1秒可以这样写
    dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC);
    dispatch_time(DISPATCH_TIME_NOW, 1000 * USEC_PER_SEC);
    dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC * NSEC_PER_USEC);

    dispatch_queue_t queue:队列
    dispatch_block_t block:block

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
         // 
    
        });
    
    4、dispatch_barrier_async

    栅栏函数,用于提交的闭包是指定队列中在特定时段唯一在执行的一个。

    注:只在自己创建的队列上有这种作用,在全局并发队列和串行队列上,效果和dispatch_sync一样
    dispatch_queue_t dataQueue = dispatch_queue_create("dataqueue", DISPATCH_QUEUE_CONCURRENT);
        // 先执行这两步
        dispatch_async(dataQueue, ^{
            [NSThread sleepForTimeInterval:2.f];
            NSLog(@"read data 1 %@",[NSThread currentThread]);
        });
        dispatch_async(dataQueue, ^{
            NSLog(@"read data 2 %@",[NSThread currentThread]);
        });
    
        // 前面执行完才执行这步
        dispatch_barrier_async(dataQueue, ^{
            NSLog(@"write data 1 %@",[NSThread currentThread]);
            [NSThread sleepForTimeInterval:1];
        });
        //再执行这步
        dispatch_barrier_async(dataQueue, ^{
            NSLog(@"write data 2 %@",[NSThread currentThread]);
            [NSThread sleepForTimeInterval:1];
        });
        // 最后再执行这两步
        dispatch_async(dataQueue, ^{
            [NSThread sleepForTimeInterval:1.f];
            NSLog(@"read data 3 %@",[NSThread currentThread]);
    
        });
        dispatch_async(dataQueue, ^{
            NSLog(@"read data 4 %@",[NSThread currentThread]);
        });
    
    5、dispatch_apply

    快速迭代

    dispatch_get_main_queue会死锁
    阻塞当前线程

     NSLog(@"开始%@",[NSThread currentThread]);
        dispatch_apply(10,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) {
            NSLog(@"%zu %@",i,[NSThread currentThread]);
        });
        NSLog(@"结束%@",[NSThread currentThread]);
    
    6、dispatch_group_t

    dispatch groups是专门用来监视多个异步任务。dispatch_group_t实例用来追踪不同队列中的不同任务。
    当group里所有事件都完成GCD API有两种方式发送通知,第一种是dispatch_group_wait,会阻塞当前进程,等所有任务都完成或等待超时。第二种方法是使用dispatch_group_notify,异步执行闭包,不会阻塞。

    dispatch_queue_t conQueue = dispatch_queue_create("xx", DISPATCH_QUEUE_CONCURRENT);
        dispatch_group_t group = dispatch_group_create();
        dispatch_group_async(group, conQueue, ^{
            NSLog(@"1 %@",[NSThread currentThread]);
        });
        dispatch_group_async(group, conQueue, ^{
            NSLog(@"2 %@",[NSThread currentThread]);
        });
        //阻塞
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    //    dispatch_group_notify(group, conQueue, ^{
    //        NSLog(@"end %@",[NSThread currentThread]);
    //    });
        NSLog(@"go on %@",[NSThread currentThread]);
    
    注:
    dispatch_group_async等价于dispatch_group_enter() 和 dispatch_group_leave()的组合。
    dispatch_group_enter() 必须运行在 dispatch_group_leave() 之前。
    dispatch_group_enter() 和 dispatch_group_leave() 需要成对出现的
    
    7、dispatch_semaphore_t

    信号量:可用来控制访问资源的数量的标识,设定了一个信号量,在线程访问之前,加上信号量的处理,可告知系统按照我们指定的信号量数量来执行多个线程。

    其实,这有点类似锁机制了,只不过信号量都是系统帮助我们处理了,我们只需要在执行线程之前,设定一个信号量值,并且在使用时,加上信号量处理方法就行了。

    //创建信号量,参数:信号量的初值,如果小于0则会返回NULL
    dispatch_semaphore_create(信号量值)
    //等待降低信号量
    dispatch_semaphore_wait(信号量,等待时间) -1
    //提高信号量
    dispatch_semaphore_signal(信号量)  +1
    
    
    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(@"run task1");
            sleep(1);
            NSLog(@"end task1");
            dispatch_semaphore_signal(semaphore);
        });
        //任务2
        dispatch_async(quene, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
             NSLog(@"run task2");
            sleep(1);
            NSLog(@"end task2");
            dispatch_semaphore_signal(semaphore);
        });
        //任务3
        dispatch_async(quene, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"run task3");
            sleep(1);
           NSLog(@"end task3");
            dispatch_semaphore_signal(semaphore);
        });
    

    控制台信息

    2018-04-17 14:44:31.909536+0800 test[3543:656486] run task1
    2018-04-17 14:44:31.909536+0800 test[3543:656487] run task2
    2018-04-17 14:44:32.912763+0800 test[3543:656486] end task1
    2018-04-17 14:44:32.912784+0800 test[3543:656487] end task2
    2018-04-17 14:44:32.912999+0800 test[3543:656488] run task3
    2018-04-17 14:44:33.916648+0800 test[3543:656488] end task3
    
    

    可以看出,我们设置的信号量为2,任务最多执行两个。当某个任务完成采取执行任务3

    参考文章:细说GCD(Grand Central Dispatch)如何用

    相关文章

      网友评论

          本文标题:多线程之GCD

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