zoukankan      html  css  js  c++  java
  • atomic的实现机制

    什么是自旋锁呢?

    锁用于解决线程争夺资源的问题,一般分为两种,自旋锁(spin)和互斥锁(mutex)。

    互斥锁可以解释为线程获取锁,发现锁被占用,就向系统申请锁空闲时唤醒他并立刻休眠。

    自旋锁比较简单,当线程发现锁被占用时,会不断循环判断锁的状态,直到获取。

    原子操作的颗粒度最小,只限于读写,对于性能的要求很高,如果使用了互斥锁势必在切换线程上耗费大量资源。相比之下,由于读写操作耗时比较小,能够在一个时间片内完成,自旋更适合这个场景。

    自旋锁的坑

    但是iOS 10之后,苹果因为一个巨大的缺陷弃用了 OSSpinLock 改为新的 os_unfair_lock

    新版 iOS 中,系统维护了 5 个不同的线程优先级/QoS: background,utility,default,user-initiated,user-interactive。高优先级线程始终会在低优先级线程前执行,一个线程不会受到比它更低优先级线程的干扰。这种线程调度算法会产生潜在的优先级反转问题,从而破坏了 spin lock。

    描述引用自 ibireme 大神的文章。

    我的理解是,当低优先级线程获取了锁,高优先级线程访问时陷入忙等状态,由于是循环调用,所以占用了系统调度资源,导致低优先级线程迟迟不能处理资源并释放锁,导致陷入死锁。

    那为什么原子操作用的还是 spinlock_t 呢?

    using spinlock_t = mutex_tt<LOCKDEBUG>;
    using mutex_t = mutex_tt<LOCKDEBUG>;
    
    class mutex_tt : nocopy_t {
        os_unfair_lock mLock; //处理了优先级的互斥锁
        void lock() {
            lockdebug_mutex_lock(this);
            os_unfair_lock_lock_with_options_inline
                (&mLock, OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION);
        }
        void unlock() {
            lockdebug_mutex_unlock(this);
            os_unfair_lock_unlock_inline(&mLock);
        }
    }
    

    差点被苹果骗了!原来系统中自旋锁已经全部改为互斥锁实现了,只是名称一直没有更改。

    https://www.cnblogs.com/vanch/p/10192002.html

    atomic作用:多线程下将属性设置为atomic可以保证读取数据的一致性。因为他将保证数据只能被一个线程占用,也就是说一个线程对属性进行写操作时,会使用自旋锁锁住该属性。不允许其他的线程对其进行读取操作了。
    但是它有一个很大的缺点:因为它要使用自旋锁锁住该属性,因此它会消耗更多的资源,性能会很低。要比nonatomic慢20倍。

    内部实现:property 的 atomic 是采用 spinlock_t (自旋锁)实现的。
    // getter方法

    id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) 
    {
        // ...
        if (!atomic) return *slot;
    
        // Atomic retain release world
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        id value = objc_retain(*slot);
        slotlock.unlock();
        // ...
    }
    

    //setter方法

    // setter
    static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy)
    {
        // ...
        if (!atomic) 
    {
            oldValue = *slot;
            *slot = newValue;
        } 
    else 
    {
            spinlock_t& slotlock = PropertyLocks[slot];
            slotlock.lock();
            oldValue = *slot;
            *slot = newValue;        
            slotlock.unlock();
        }
        // ...
    }
    
    

    小结

    简而言之,atomic的作用只是给getter和setter加了个锁,atomic只能保证代码进入getter或者setter函数内部时是安全的,一旦出了getter和setter,多线程安全只能靠程序员自己保障了。所以atomic属性和使用property的多线程安全并没什么直接的联系。另外,atomic由于加锁也会带来一些性能损耗,所以我们在编写iOS代码的时候,一般声明property为nonatomic,在需要做多线程安全的场景,自己去额外加锁做同步。



    作者:CharmecarWang
    链接:https://www.jianshu.com/p/7ca19d0ad176
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    ------------------越是喧嚣的世界,越需要宁静的思考------------------ 合抱之木,生于毫末;九层之台,起于垒土;千里之行,始于足下。 积土成山,风雨兴焉;积水成渊,蛟龙生焉;积善成德,而神明自得,圣心备焉。故不积跬步,无以至千里;不积小流,无以成江海。骐骥一跃,不能十步;驽马十驾,功在不舍。锲而舍之,朽木不折;锲而不舍,金石可镂。蚓无爪牙之利,筋骨之强,上食埃土,下饮黄泉,用心一也。蟹六跪而二螯,非蛇鳝之穴无可寄托者,用心躁也。
  • 相关阅读:
    python lambda函数的用法
    python 中is 和 ==的区别
    Mongo 聚合函数 $group方法的使用
    mongo聚合
    当mongo数据库报错关于 Failed global initialization:
    python 中字符串的拼接
    python eval()用法报错 SyntaxError: unexpected EOF while parsing
    高性能MySQL(六):选择合适的存储引擎
    高性能MySQL(五):存储引擎
    高性能MySQL(四):多版本并发控制
  • 原文地址:https://www.cnblogs.com/feng9exe/p/14541048.html
Copyright © 2011-2022 走看看