美文网首页
问题:苹果为什么要废弃dispatch_get_current_

问题:苹果为什么要废弃dispatch_get_current_

作者: 姜小舟 | 来源:发表于2020-05-18 17:49 被阅读0次

    dispatch_get_current_queue容易造成死锁

    Recommended for debugging and logging purposes only: The code must not make any assumptions about the queue returned, unless it is one of the global queues or a queue the code has itself created. The code must not assume that synchronous execution onto a queue is safe from deadlock if that queue is not the one returned by dispatch_get_current_queue().
    该方法早在iOS 6.0就已被废弃,仅推荐用于调试和日志记录,不能依赖函数返回值进行逻辑判断。具体为什么被废弃,文档并没有详细说明,我们可以从GCD源码找到些线索,代码在libdispatch源码可以下载。

    在讨论之前,我们需要了解一个概念:可重入
    维基百科:
    若一个程序或子程序可以“安全的被并行执行(Parallel computing)”,则称其为可重入(reentrant或re-entrant)的。

    即:当该子程序正在运行时,可以再次进入并执行它(并行执行时,个别的执行结果,都符合设计时的预期)。

    若一个函数是可重入的,则该函数:
    • 不能含有静态(全局)非常量数据
    • 不能返回静态(全局)非常量数据的地址
    • 只能处理由调用者提供的数据
    • 不能依赖于单实例模式资源的锁
    • 不能调用(call)不可重入的函数(有呼叫(call)到的函数需满足前述条件)

    有时候我们很希望知道当前执行的queue是谁,比如UI操作需要放在main queue中执行。如果可以知道当前工作的queue是谁,就可以在特定的queue中执行相应的操作。 而dispatch_get_current_queue() 正好能帮上忙:

    void func(dispatch_queue_t queue, dispatch_block_t block) {
       if (dispatch_get_current_queue() == queue) {
           block();
       }else{
           dispatch_sync(queue, block);
       }
    }
    

    然后潜意识里,觉得这个函数是可重入的。

    但当target queue恰好是current queue时,同步阻塞会导致死锁。

    - (void)deadLockFunc {
       dispatch_queue_t queueA = dispatch_queue_create("com.yiyaaixuexi.queueA", NULL);
       dispatch_queue_t queueB = dispatch_queue_create("com.yiyaaixuexi.queueB", NULL);
       dispatch_sync(queueA, ^{
           dispatch_sync(queueB, ^{
               dispatch_block_t block = ^{
                   //do something
               };
               func(queueA, block);
           });
       });
    }
    

    问题出在GCD队列本身是不可重入的,串行同步队列的层级关系,是出现问题的根本原因。

    为了防止类似的误用,苹果在iOS6废弃了dispatch_get_current_queue()方法。强大的dispatch_get_current_queue()也只能当作一个调试工具了。

    拓展:那么应该如何保证GCD方法可重入呢?

    • dispatch_queue_set_specific 标记队列
    dispatch_queue_t queueA = dispatch_queue_create("com.yiyaaixuexi.queueA", NULL);
    dispatch_queue_t queueB = dispatch_queue_create("com.yiyaaixuexi.queueB", NULL);
    dispatch_set_target_queue(queueB, queueA);
       
    static int specificKey;
    CFStringRef specificValue = CFSTR("queueA");
    dispatch_queue_set_specific(queueA,
                                &specificKey,
                                (void*)specificValue,
                                (dispatch_function_t)CFRelease);
       
    dispatch_sync(queueB, ^{
        dispatch_block_t block = ^{
            //do something
        };
        CFStringRef retrievedValue = dispatch_get_specific(&specificKey);
        if (retrievedValue) {
            block();
        } else {
            dispatch_sync(queueA, block);
        }
    });
    
    • 递归锁
    void dispatch_reentrant(void (^block)()) {
        static NSRecursiveLock *lock = nil;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            lock = [[NSRecursiveLock alloc]init];
        });
        [lock lock];
        block();
        [lock unlock];
    }
    
    - (void)testFunc {
        dispatch_queue_t queueA = dispatch_queue_create("com.yiyaaixuexi.queueA", NULL);
        dispatch_block_t block = ^{
             //do something
        };
        dispatch_sync(queueA, ^{
            dispatch_reentrant(block);
        }); 
    }
    

    相关文章

      网友评论

          本文标题:问题:苹果为什么要废弃dispatch_get_current_

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