美文网首页开发应用
二、atomic 实现机制

二、atomic 实现机制

作者: 流小贝 | 来源:发表于2022-04-30 09:22 被阅读0次

    atomic作用:

    多线程下将属性设置为atomic可以保证读取数据的一致性。因为他将保证数据只能被一个线程占用,也就是说一个线程对属性进行写操作时,会使用自旋锁锁住该属性。不允许其他的线程对其进行读取操作了。

    但是它有一个很大的缺点:因为它要使用自旋锁锁住该属性,因此它会消耗更多的资源,性能会很低。要比nonatomic慢20倍。

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

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

    指针Property指向的内存区域

    property可以分为值类型和引用类型。值类型就是一些基本的数据类型。而引用类型就是指的各种对象。声明为指针,指向这个属性所在的内存区域。

    因此当我们访问一个属性的时候,有可能访问的是指针本身,还有可能访问的是这个指针指向的内存区域。

    self.userName=@"nick"; -- 这里访问的就是指针本身,对指针进行赋值。

    [self.userName rangeOfString:@"peak"]; --这里是在访问指针指向的字符串所在的内存区域。

    指针本身也是占用内存的,是固定的8个字节。

    而指针指向的内存区域占用的内存就无法固定了,有可能很大,也有可能很小。我们暂且定为n。

    @property(atomic)NSArray *array;

    atomic修饰的实际上是这个指针,也就是占8个字节内存的指针,因此就不可能随意使用多线程来操作这块内存的。因为这块内存是原子性的。是线程安全的。

    真正不安全的是指针指向的那块内存区域,他是非原子性的,当多个线程去操作这块内存的时候,就会出现不安全的情况。

    所以真正要操心的是这一类内存区域的访问,即使用atomatic声明属性也没用。

    如何做到多线程安全?(加锁)

    什么是自旋锁呢?

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

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

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

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

    atomic的实现机制

    用atomic修饰的值类型属性赋值的时候,系统会给getter和setter方法里面加一个锁,来保证同时只有一个线程能访问或者修改该属性。保证读写的原子性。所以atomic能保证属性的存取时是线程安全的。

    atomic为什么不能保证绝对的线程安全?

    1 因为atomic给getter和setter方法加了锁,但是只能保证存取是安全的,如果我此时创建了一个用atomic修饰的可变数组,atomic无法保证在对数组进行修改是线程安全的;

    2 在编译器自己创建的getter/setter方法里面,最终会调到objc_getProperty和objc_setProperty方法,在这两个方法内部,atomic保证了读写线程的安全。但是,如果我重写了getter/setter方法,那atomic对getter/setter的保护也就没了,这时候就需要自己去维护线程安全了。

    相关文章

      网友评论

        本文标题:二、atomic 实现机制

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