GCD

作者: 答案不止一个 | 来源:发表于2020-11-07 16:46 被阅读0次

    GCD 调度队列是一个类似对象的结构,用于管理您提交给它的任务。所有调度队列都是先进先出数据结构。因此,添加到队列中的任务始终以与添加任务相同的顺序启动。

    在当前的OS X和iOS中,ARC现在将Dispatch对象视为Obj-C对象。它们将以与Obj-C对象相同的方式进行内存管理,因此应将其strong用于属性。
    这由中OS_OBJECT_USE_OBJC定义的宏控制<os/object.h>。1当您的部署目标是OS X 10.8或更高版本或iOS 6.0或更高版本时,默认情况下将其设置为该值。

    @property (strong, nonatomic) dispatch_queue_t queue;
    
    内存的释放

    调度队列对自动释放的对象提供的支持最少。系统API可能会将自动释放的对象返回到您的代码中。例如,NSError对象通常是自动释放的。如果由于块中创建了自动释放的对象而导致内存压力增加,请考虑将自动释放池添加到这些块中以减轻压力。您还可以在创建时使用该功能配置任何自定义调度队列的默认自动释放行为。 dispatch_queue_attr_make_with_autorelease_frequency

    dispatch queue

    四种类型 dispatch_queue_t 定义
    1. dispatch_queue_serial_t serial 串行队列。一次只能执行一个任务(完成才能执行下一个),分派队列,按先进先出(FIFO)顺序连续执行任务。
    2. dispatch_queue_concurrent_t concurrent 并行队列。可以同事执行多个任务
    3. dispatch_queue_main_t main 主队列。全局可用的一个串行队列。这个队列和主线程的运行循环一起工作。
    4. dispatch_queue_global_t global queue 具有指定服务质量类的系统定义的全局并发队列

    由此底层是只有两种形式的队列,并发队列和串行队列

    创建获取队列

    1. dispatch_get_main_queue() 返回与应用程序主线程关联的串行调度队列。
    2. dispatch_get_global_queue 返回具有指定服务质量类的系统定义的全局并发队列。
    3. dispatch_queue_create 创建一个新的调度队列,您可以向其提交块。
    4. dispatch_queue_create_with_target 创建一个新的调度队列,您可以向其提交块。
    执行
    异步执行
    • dispatch_async 提交一个块以在调度队列上异步执行并立即返回。
    • dispatch_async_f 提交应用程序定义的函数以在调度队列上异步执行并立即返回。接收一个 dispatch_function_t 类型的参数作为函数,以及一个 context 数据
    // dispatch_function_t 是一个 block 代码块的定义
    typedef void (*dispatch_function_t)(void *_Nullable);
    // context 是一个数据的指针地址,作为后面 word 代码块的参数 
    void dispatch_async_f(dispatch_queue_t queue,
            void *_Nullable context, dispatch_function_t work);
    // 事例
    void wrapper(void* a) {
        NSLog(@"as,%@",a);
    };
    NSObject *vvc = objc;
    dispatch_async_f(queue, (__bridge void * _Nullable)(vvc) ,&wrapper);
    
    
    • dispatch_after 使块在指定的时间执行。
    • dispatch_after_f 使应用程序定义的函数入队以在指定时间执行。
    同步执行
    • dispatch_sync
    • dispatch_sync_f
    • dispatch_apply 同步的方式将代码块加入的 队列中执行指定次数。只有当传入的代码块执行完成之后,才能继续向下执行。 void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t));
    • dispatch_apply_f

    DISPATCH_APPLY_AUTO

    获取和设置上下文数据
    • dispatch_get_specific 返回与当前调度队列关联的键的值。
    • dispatch_queue_set_specific 设置指定调度队列的键/值数据。
    • dispatch_queue_get_specific 获取与指定的调度队列关联的键的值。
        void wrapper(void* a) {
            NSLog(@"as,%@",a);
        };
        
        const char * lable = "asdasdd";
        dispatch_queue_t queue = dispatch_queue_create(lable, DISPATCH_QUEUE_CONCURRENT);
        dispatch_queue_set_specific(queue, lable, CFBridgingRetain(self), &wrapper);
        dispatch_apply(20, DISPATCH_APPLY_AUTO, ^(size_t t) {
            void * x = dispatch_queue_get_specific(queue, lable);
            NSLog(@"%@",x);
        });
    

    Dispatch Work Item

    可以用来添加依赖项 或者监控句柄(之前或者执行完成之后)

    创建

    • dispatch_block_create 创建一个调度块. dispatch_block_flags_t block的标志。比如优先级,上下文等
    • dispatch_block_create_with_qos_class
    dispatch_block_t dispatch_block_create(dispatch_block_flags_t flags, dispatch_block_t block);
    
    dispatch_block_t dispatch_block_create_with_qos_class(dispatch_block_flags_t flags,
            dispatch_qos_class_t qos_class, int relative_priority,
            dispatch_block_t block);
     
    DISPATCH_OPTIONS(dispatch_block_flags, unsigned long,
        // 指示调度块对象应充当屏障块的标志。要在 dispatch_barrier_async 中使用,直接使用无效
        DISPATCH_BLOCK_BARRIER 
        // 
        DISPATCH_BLOCK_DETACHED
                DISPATCH_ENUM_API_AVAILABLE(macos(10.10), ios(8.0)) = 0x2,
        DISPATCH_BLOCK_ASSIGN_CURRENT
                DISPATCH_ENUM_API_AVAILABLE(macos(10.10), ios(8.0)) = 0x4,
        DISPATCH_BLOCK_NO_QOS_CLASS
                DISPATCH_ENUM_API_AVAILABLE(macos(10.10), ios(8.0)) = 0x8,
        DISPATCH_BLOCK_INHERIT_QOS_CLASS
                DISPATCH_ENUM_API_AVAILABLE(macos(10.10), ios(8.0)) = 0x10,
        DISPATCH_BLOCK_ENFORCE_QOS_CLASS
                DISPATCH_ENUM_API_AVAILABLE(macos(10.10), ios(8.0)) = 0x20,
    );
    
    dispatch_block_wait

    等待一个 dispatch_block_t 的执行完成。如果对应的block 没有加入到对立中执行,就会等待超时时间, 返回大于0,如果已经完成,就直接返回0, 会阻塞当前队列。相当于 dispatch_sync串行。可以用于代码块的以来关系

        dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT);
    
        dispatch_async(concurrentQuene, ^{
            dispatch_queue_t allTasksQueue = dispatch_queue_create("allTasksQueue", DISPATCH_QUEUE_CONCURRENT);
    
            dispatch_block_t block = dispatch_block_create(0, ^{
                NSLog(@"开始执行");
                [NSThread sleepForTimeInterval:2];
                NSLog(@"结束执行");
            });
            
            dispatch_async(allTasksQueue, block);
            dispatch_sync(allTasksQueue, ^{
                sleep(3);
            });
            dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC));
            long resutl = dispatch_block_wait(block, timeout);
    
            // 等待时长,10s 之后超时
           
            
            if (resutl == 0) {
                NSLog(@"执行成功");
            } else {
                NSLog(@"执行超时");
            }
        });
    
    dispatch_block_perform

    以特定标志运行block。等同于代码

    dispatch_block_t b = dispatch_block_create(flags, block);
    b();
    Block_release(b);
    
    dispatch_block_notify

    当置顶的代码块完成之后,执行监听的代码块。 注意第一个要监听的代码块需要已经加入到对队列中,否则会直接执行监听的程序。可以用于依赖的实现

        dispatch_queue_t queue = dispatch_queue_create("asddd", DISPATCH_QUEUE_CONCURRENT);
    
        dispatch_block_t t = dispatch_block_create(0, ^{
            NSLog(@"1");
            sleep(2);
            NSLog(@"3");
        });
        dispatch_async(queue, t);
        dispatch_block_notify(t, dispatch_queue_create("asdd", DISPATCH_QUEUE_CONCURRENT), ^{
            NSLog(@"完成");
        });
    
    dispatch_block_cancel 异步取消指定的调度块。
    dispatch_block_testcancel 测试给定的调度块是否已取消。

    Dispatch Group

    可以将不同队列中的多个块附加到一个组上,用于等待处理所有块执行完成后的处理程序。并且 只会监听 dispatch_group_async 加入的block。不会观察queue中其他的block对象

        dispatch_queue_t queue = dispatch_queue_create("asddd", DISPATCH_QUEUE_CONCURRENT);
    
        dispatch_block_t t = dispatch_block_create(0, ^{
            printf("\n queue block");
            sleep(3);
            printf("\n queue block finish");
        });
        dispatch_async(queue, t);
        dispatch_block_notify(t, dispatch_queue_create("asdd", DISPATCH_QUEUE_CONCURRENT), ^{
            printf("\n queue block finish notifiy");
        });
        
    
        dispatch_group_t group = dispatch_group_create();
        dispatch_group_async(group, queue, ^{
            printf("\n group block");
            sleep(1);
            printf("\n group block finish");
        });
        dispatch_group_notify(group, queue, ^{
            printf("\n group block finish notify");
        });
        
     queue block
     group block
     group block finish
     group block finish notify
     queue block finish
     queue block finish notifiy
        
    

    类似于 workitem 也有相似的 wait, 和notify 程序用户管理组的处理

        // 对于queue 并不是执行的监控queue, 而是监控完成时,指定监控代码块再 queue 的队列中执行
        dispatch_group_notify(group, queue, ^{
            NSLog(@"完成1");
        });
    
    • dispatch_group_notify 计划一组先前提交的块对象完成后将要提交给队列的块对象。
    • dispatch_group_notify_f 调度一组先前提交的块对象完成后,将应用程序定义的函数提交给队列。
    • dispatch_group_wait 同步等待先前提交的块对象完成;如果块在指定的超时时间之前未完成,则返回。
    dispatch_group_enter / dispatch_group_leave

    对于不是通过 dispatch_group_async 加入的代码块,或者其他方法,也想通过 group 进行管理监听 任务组完成,可以使用 dispatch_group_enter 和 dispatch_group_leave。

    dispatch_group_enter 和 dispatch_group_leave 要成对出现,如果 dispatch_group_enter 比较多,则会任务组不会触发 dispatch_group_notify ,wait 会超时。如果 leave 多,会崩溃。 一个类似于上面的例子。

        dispatch_queue_t queue = dispatch_queue_create("asddd", DISPATCH_QUEUE_CONCURRENT);
        dispatch_group_t group = dispatch_group_create();
        
        dispatch_block_t t = dispatch_block_create(0, ^{
            dispatch_group_enter(group);
            printf("\n queue block");
            sleep(3);
            printf("\n queue block finish");
            dispatch_group_leave(group);
        });
        dispatch_async(queue, t);
        dispatch_block_notify(t, dispatch_queue_create("asdd", DISPATCH_QUEUE_CONCURRENT), ^{
            printf("\n queue block finish notifiy");
        });
     // 此时的 group notify 会在 queue的block leave() 执行之后才会 执行 
     queue block
     group block
     group block finish
     queue block finish
     group block finish notify
     queue block finish notifiy
     
    // 如果 leave 再等待3s 之前执行
        dispatch_queue_t queue = dispatch_queue_create("asddd", DISPATCH_QUEUE_CONCURRENT);
        dispatch_group_t group = dispatch_group_create();
        
        dispatch_block_t t = dispatch_block_create(0, ^{
            dispatch_group_enter(group);
            printf("\n queue block");
            dispatch_group_leave(group);
            sleep(3);
            printf("\n queue block finish");
            
        });
        dispatch_async(queue, t);
        dispatch_block_notify(t, dispatch_queue_create("asdd", DISPATCH_QUEUE_CONCURRENT), ^{
            printf("\n queue block finish notifiy");
        });
        
     queue block
     group block
     group block finish
     group block finish notify
     queue block finish
     queue block finish notifiy
    

    系统事件监控

    dispatch source

    创建一个新的调度源以监视低级系统事件。

    创建 dispatch_source_create
    // dispatch_source_type_t 调度源正在监视的系统对象类型的标识符
    // handle 要监视的基础系统句柄。此参数的解释由type参数中提供的常量确定。可以传0 
    // mask 指定所需事件的标志掩码。此参数的解释由type参数中提供的常量确定。
    // queue 时间处理的调度队列
    dispatch_source_t dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, uintptr_t mask, dispatch_queue_t queue);
    
    • type dispatch源可处理的事件
    • handle 可以理解为句柄、索引或id,假如要监听进程,需要传入进程的ID
    • mask 可以理解为描述,提供更详细的描述,让它知道具体要监听什么
    • queue 自定义源需要的一个队列,用来处理所有的响应句柄(block)
    dispatch_source_type_t Dispatch Source可处理的事件
    • DISPATCH_SOURCE_TYPE_DATA_ADD 自定义的事件,变量增加
    • DISPATCH_SOURCE_TYPE_DATA_OR 自定义的事件,变量OR
    • DISPATCH_SOURCE_TYPE_MACH_SEND MACH端口发送
    • DISPATCH_SOURCE_TYPE_MACH_RECV MACH端口接收
    • DISPATCH_SOURCE_TYPE_PROC 进程监听,如进程的退出、创建一个或更多的子线程、进程收到UNIX信号
    • DISPATCH_SOURCE_TYPE_READ IO操作,如对文件的操作、socket操作的读响应
    • DISPATCH_SOURCE_TYPE_SIGNAL 接收到UNIX信号时响应
    • DISPATCH_SOURCE_TYPE_TIMER 定时器
    • DISPATCH_SOURCE_TYPE_VNODE 文件状态监听,文件被删除、移动、重命名
    • DISPATCH_SOURCE_TYPE_WRITE IO操作,如对文件的操作、socket操作的写响应

    DISPATCH_SOURCE_TYPE_DATA_ADD
    当同一时间,一个事件的的触发频率很高,那么Dispatch Source会将这些响应以ADD的方式进行累积,然后等系统空闲时最终处理,如果触发频率比较零散,那么Dispatch Source会将这些事件分别响应。

    DISPATCH_SOURCE_TYPE_DATA_OR 和上面的一样,是自定义的事件,但是它是以OR的方式进行累积

    设置source 相关heander
    • dispatch_source_set_registration_handler_f 设置给定调度源的注册处理函数。
    • dispatch_source_set_registration_handler 设置给定调度源的注册处理程序块。
    • dispatch_source_set_event_handler_f 设置给定调度源的事件处理函数。
    • dispatch_source_set_event_handler 为给定的调度源设置事件处理程序块。
    • dispatch_source_set_cancel_handler_f 设置给定调度源的取消处理函数。
    • dispatch_source_set_cancel_handler 设置给定调度源的取消处理程序块。
    dispatch_source_merge_data 和 dispatch_source_get_data 对应会发生

    对于 DISPATCH_SOURCE_TYPE_DATA_ADD,DISPATCH_SOURCE_TYPE_DATA_OR, DISPATCH_SOURCE_TYPE_DATA_REPLACE 这三种处理时间,可以使用 dispatch_source_merge_data 进行触发。 然后在 dispatch_source_set_event_handler 设置的方法中根据 dispatch_source_get_data 获取处理过后的数据

    1. add 会对触发的数据相加传入
    2. or 试对触发的数据进行 or 运算
    3. replace 则试后面的替换掉前面的 data
        dispatch_queue_t queue = dispatch_queue_create("sss", DISPATCH_QUEUE_CONCURRENT);
        // 防止source 销毁
        self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_REPLACE, 0, 0, queue);
        
        dispatch_source_set_event_handler(self.source, ^{
            intptr_t d = dispatch_source_get_data(wself.source);
            printf("\n-- %zu   %d \n", d);
        });
        
        dispatch_async(queue, ^{
            for(int i =0 ;i < 100; i++){
                printf("%d",i);
                dispatch_source_merge_data(wself.source, i);
                sleep(0.1);
            }
        });
        // 启动source
        dispatch_resume(self.source);
        
    
    DISPATCH_SOURCE_TYPE_TIMER 设置timer 循环
        dispatch_queue_t queue = dispatch_queue_create("sss", DISPATCH_QUEUE_CONCURRENT);
        self.source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        
        dispatch_source_set_timer(self.source, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0);
        
        __weak ViewController * wself = self;
        self.a = 10;
        
        dispatch_source_set_event_handler(self.source, ^{
            uintptr_t data = dispatch_source_get_data(wself.source);
            printf("\n dispatch_source_set_event_handler %d  %zu ", wself.a, data);
            wself.a = wself.a - 1;
            if(wself.a <= 0){
                dispatch_source_cancel(wself.source);
            }
        });
        
        dispatch_source_set_cancel_handler(self.source, ^{
            printf("\n dispatch_source_set_cancel_handler");
        });
        
        dispatch_source_set_cancel_handler(self.source, ^{
            printf("\n dispatch_source_set_cancel_handler");
        });
        
        dispatch_resume(self.source);
    
    取消
    • dispatch_source_cancel 异步取消调度源,以防止进一步调用其事件处理程序块。
    • dispatch_source_testcancel 测试给定的调度源是否已取消。

    Dispatch Data

    DispatchSemaphore 信号量

    1. 创建 dispatch_semaphore_create(int) 传入一个大于0 的整数
    2. 去判断等待 dispatch_semaphore_wait
    3. 释放信号量 dispatch_semaphore_signal
    面试题
    1. 异步的队列 可能打印的结果。根据能确定的 同步执行,确定可能的先后顺序
        dispatch_queue_t queue = dispatch_queue_create("concurrent",     DISPATCH_QUEUE_CONCURRENT);
        dispatch_async(queue, ^{
            NSLog(@"1");
        });
        dispatch_async(queue, ^{
            NSLog(@"2");
        });
        NSLog(@"0");
        dispatch_async(queue, ^{
            NSLog(@"3");
        });
        dispatch_async(queue, ^{
            NSLog(@"4");
        });
        // 唯一可以确定的是 0 一定再 3 和 4 之前
    
    耗能

    只要调用 同步调度异步调度 都会耗时。 计算 dispatch_async的执行时间 。

    同步 异步执行 可能出现的 堵塞
    文档

    相关文章

      网友评论

          本文标题:GCD

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