美文网首页
45.使用dispatch_once来执行只需运行一次的线程安全

45.使用dispatch_once来执行只需运行一次的线程安全

作者: z_zero | 来源:发表于2016-08-20 00:14 被阅读0次

    《编写高质量iOS与OS X代码的52个有效方法》--第六章 第45条
    (ps:此乃读书笔记,加深记忆,仅供大家参考)


    第45条:使用dispatch_once来执行只需运行一次的线程安全代码

    单例模式(singleton)对Objective-C开发者来说并不陌生,常见的实现方式为:在类中编写名为sharedInstance的方法,该方法只会返回全类共用的单例实例,而不会在每次调用时都创建新的实例。共享实例的方法一般又会这样写:

    @implementation EOCClass
    
    + (instancetype)sharedInstance
    {
        static EOCClass *sharedInstance = nil;
        @synchronized (self) {
            if (!sharedInstance) {
                sharedInstance = [[self alloc] init];
            }
        }
        return sharedInstance;
    }
    @end
    

    笔者发现单例模式容易引起激烈争论,Objective-C的单例尤其如此。线程安全是大家争论的主要问题。为保证线程安全,上述代码将创建单例实例的代码包裹在同步块里。

    不过GCD引入了一项特性,能使单例实现起来更为容易。所用的函数是:

    void dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);
    

    此函数接受类型为dispatch_once_t的特殊参数,笔者称其为“标记”(token),此外还接受参数。对于给定的标记来说,该函数保证相关的块必定会执行,且仅执行一次。首次调用该函数时,必然会执行块中的代码,最重要的一点在于,此操作完全是线程安全的。请注意,对于只需执行一次的块来说,每次调用函数时传入的标记都必须完全相同。因此,开发者通常将标记变量声明在static或global作用域里。

    刚才实现单例模式所用的sharedInstance方法,可以用此函数来改写:

    + (instancetype)sharedInstance
    {
        static EOCClass *sharedInstance = nil;
        static dispatch_once_t onceToken;
    
        dispatch_once(&onceToken, ^{
            sharedInstance = [[self alloc] init];
        });
    
        return sharedInstance;
    }
    

    使用dispatch_once可以简化代码并且彻底保证线程安全,开发者根本无须担心加锁或同步。所有问题都有GCD在底层处理。由于每次调用时都必须使用完全相同的标记,所以标记要声明成static。把该变量定义在static作用域中,可以保证编译器在每次执行shareInstance方法时都会复用这个变量,而不会创建新变量。

    此外,dispatch_once更高效。他没有使用重量级的同步机制,若是那样的话,每次运行代码前都要获取锁,相反,此函数采用“原子访问”(atomic access)来查询标记,以判断其所对应的代码原来是否已经执行过。

    要点

    • 经常需要编写“只需执行一次的线程安全代码”(thread-safe single-code execution)。通过GCD所提供的dispatch_once函数,很容易就能实现此功能。
    • 标记应该声明在static或global作用域中,这样的话,在把只需执行一次的块传给dispatch_once函数时,穿进去的标记也是相同的。

    相关文章

      网友评论

          本文标题:45.使用dispatch_once来执行只需运行一次的线程安全

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