atomic - 源码解析

465 阅读1分钟

概要

atomic只是一个锁,具体一点地说就是一个spinlock自旋锁,而线程安全是利用锁来构建的一种代码模式,两者在逻辑上不对等

例子1

针对某一个属性,若线程A在调getter的同时,线程B和C都调了该属性的setter,那线程A能get到3种值:原始值、线程B设置的值、线程C设置的值

但因为线程A在一定概率下能获取正确的值,所以不能100%证明atomic不是线程安全的

例子2

@property(atomic, strong) NSString* str;

/// thread A
for (int i = 0; i < 100000; i++) {
    if (i % 2 == 0) {
        self.str = @"0123456789";
    } else {
        self.str = @"01234";
    }
    NSLog(@"Thread A: %@", self.str);
}

/// thread B
for (int i = 0; i < 100000; i++) {
    if (self.str.length >= 10) {
        NSString *subStr = [self.str substringWithRange:NSMakeRange(0, 10)];
    }
    NSLog(@"Thread B: %@", self.str);
}

线程B在生成subStr时肯定会因为str长度不够而Crash,能100%证明atomic不是线程安全的

atomic源码解析

本文基于runtime源码818.2版本做分析

在objc-accessors.mm中可以看到atomic的实现,代码如下

id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) {
    if (offset == 0) {
        return object_getClass(self);
    }

    // Retain release world
    id* slot = (id*)((char*)self + offset);
    if (!atomic) return *slot;

    // Atomic retain release world
    spinlock_t& slotlock = PropertyLocks[slot];
    slotlock.lock();
    id value = objc_retain(*slot);
    slotlock.unlock();

    // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock
    return objc_autoreleaseReturnValue(value);
}

static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) {
    if (offset == 0) {
        object_setClass(self, newValue);
        return;
    }

    id oldValue;
    id* slot = (id*)((char*)self + offset);

    if (copy) {
        newValue = [newValue copyWithZone:nil];
    } else if (mutableCopy) {
        newValue = [newValue mutableCopyWithZone:nil];
    } else {
        if (*slot == newValue) return;
        newValue = objc_retain(newValue);
    }

    if (!atomic) {
        oldValue = *slot;
        *slot = newValue;
    } else {
        spinlock_t& slotlock = PropertyLocks[slot];
        slotlock.lock();
        oldValue = *slot;
        *slot = newValue;
        slotlock.unlock();
    }

    objc_release(oldValue);
}

atomic对属性的setter和getter方法添加spinlock自旋锁,但其实spinlock由于存在优先级反转问题已经被弃用,在其内部实现里已经被os_unfair_lock替代

参考从源代码理解atomic为什么不是线程安全