美文网首页编写高质量代码的52个有效方法
52个有效方法(46) - 不要使用dispatch_get_c

52个有效方法(46) - 不要使用dispatch_get_c

作者: SkyMing一C | 来源:发表于2018-10-04 22:24 被阅读14次
    可重入
    • 来自维基百科:若一个程序或子程序可以“安全的被并行执行(Parallel computing)”,则称其为可重入(reentrant或re-entrant)的。

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

    • 若一个函数是可重入的,则该函数:

      • 不能含有静态(全局)非常量数据。

      • 不能返回静态(全局)非常量数据的地址只能处理由调用者提供的数据。

      • 不能依赖于单实例模式资源的锁。

      • 不能调用(call)不可重入的函数(有呼叫(call)到的函数需满足前述条件)。

    dispatch_get_current_queue

    使用GCD的时候经常要判断当前代码是在哪个队列上执行的,会发现有下面这个函数:

     dispatch_queue_t dispatch_get_current_queue();
    

    iOS 6.0开始已经弃用这个函数了,它检测当前队列是不是某个特定的队列。但在使用dispatch_get_current_queue来判断是否当前线程返回的可能不是你想要的结果。

    -(void)demo1{
        dispatch_queue_t queueA = dispatch_queue_create("com.lyk.queueA", NULL);
        dispatch_queue_t queueB = dispatch_queue_create("com.lyk.queueB", NULL);
        
        dispatch_sync(queueA, ^{
           dispatch_sync(queueB, ^{
              dispatch_sync(queueA, ^{
                 //......
              });
           });
        });
    }
    
    • 到最内层的派发队列时,会死锁。A在等B,B在等A,A阻塞。
    -(void)demo2{
        dispatch_queue_t queueA = dispatch_queue_create("com.sky.queueA", NULL);
        dispatch_queue_t queueB = dispatch_queue_create("com.sky.queueB", NULL);
        
        dispatch_sync(queueA, ^{
            dispatch_sync(queueB, ^{
                dispatch_block_t block = ^{};
                if (dispatch_get_current_queue() == queueA) {
                    block();
                } else {
                    dispatch_sync(queueA, block);
                }
            });
        });
    }
    
    
    
    • dispatch_get_current_queue获取到的当前队列是queueB,所以结果依然执行针对queueA的同步派发操作,依然死锁。
    GCD方法可重入
    • dispatch_queue_set_specific 标记队列
     dispatch_queue_t queueA = dispatch_queue_create("com.sky.queueA", NULL);
        dispatch_queue_t queueB = dispatch_queue_create("com.sky.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];
    }
     
        dispatch_queue_t queueA = dispatch_queue_create("com.yiyaaixuexi.queueA", NULL);
        dispatch_block_t block = ^{
             //do something
        };
        dispatch_sync(queueA, ^{
            dispatch_reentrant(block);
        }); 
    
    要点
    1. dispatch_get_current_queue函数的行为常常与开发者所预期的不同。此函数已经废弃,只应做调试使用。

    2. 由于派发队列是按层级来组织的,所以无法单用某个队列对象来描述“当前队列”这一概念。

    3. dispatch_get_current_queue函数用于解决由不可重入的代码所引发的死锁,然而能用此函数解决的问题,通常也能改用“队列特定数据”来解决。

    相关文章

      网友评论

        本文标题:52个有效方法(46) - 不要使用dispatch_get_c

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