美文网首页
从atomic说起

从atomic说起

作者: AprSnow | 来源:发表于2018-03-22 16:03 被阅读21次

前言

本文从atomicnonatomic 的区别说起,又叙述了一下@synchronized的作用。

atomicnonatomic 属性表示变量的原子性,nonatomic 表示不保证线程安全,但其实atomic也不能一定保证线程安全。

具体看一下编译器对atomicnonatomic 做了什么。

nonatomic

如果一个属性为 nonatomic 时,默认的settergetter 如下:

@property(nonatomic, retain) UITextField *userName;

- (UITextField *) userName {
    return userName;
}

- (void) setUserName:(UITextField *)userName_ {
    [userName_ retain];
    [userName release];
    userName = userName_;
}

atomic

如果一个属性为 nonatomic 时,默认的settergetter 如下:

@property(retain) UITextField *userName;

- (UITextField *) userName {
    UITextField *retval = nil;
    @synchronized(self) {
        retval = [[userName retain] autorelease];
    }
    return retval;
}

- (void) setUserName:(UITextField *)userName_ {
    @synchronized(self) {
      [userName_ retain];
      [userName release];
      userName = userName_;
    }
}

可以看出相对于 nonatomic ,此时的 settergetter 中多了一个 @synchronized(self)

@synchronized 的作用是创建一个互斥锁,保证此时没有其它线程对 self 对象进行修改,其实这样子只保证了gettersetter存取方法的线程安全,并不能保证整个对象是线程安全的。

比如,线程A对一个变量write后,线程B再对同一个变量write,之后线程A对此变量read,这样子线程A会read到线程B的数据,显然是不安全的。

那么@synchronized 具体又是做什么的?

synchronized

编译器遇到@synchronized block

@synchronized(self) {
    // code
  }

会转化为如下的代码,

{
  objc_sync_enter(self)
  // code
  objc_sync_exit(self);
}

可以看到,这里有两个函数 objc_sync_enter , objc_sync_exit,通过源码objc4-680来查看这两个函数:

int objc_sync_enter(id obj)
{
    int result = OBJC_SYNC_SUCCESS;

    if (obj) {
        SyncData* data = id2data(obj, ACQUIRE);
        assert(data);
        data->mutex.lock();
    } else {
        // @synchronized(nil) does nothing
        if (DebugNilSync) {
            _objc_inform("NIL SYNC DEBUG: @synchronized(nil); set a breakpoint on objc_sync_nil to debug");
        }
        objc_sync_nil();
    }

    return result;
}
int objc_sync_exit(id obj)
{
    int result = OBJC_SYNC_SUCCESS;
    
    if (obj) {
        SyncData* data = id2data(obj, RELEASE); 
        if (!data) {
            result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
        } else {
            bool okay = data->mutex.tryUnlock();
            if (!okay) {
                result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
            }
        }
    } else {
        // @synchronized(nil) does nothing
    }
    

    return result;
}

从源码可以看出,有一个互斥锁data->mutex.lock(), data->mutex.tryUnlock() 来保证线程安全。

同时@synchronized(nil)是不起任何作用的。

继续看源码,发现mutex是一个递归锁,

typedef struct SyncData {
    struct SyncData* nextData;
    DisguisedPtr<objc_object> object;
    int32_t threadCount;  // number of THREADS using this block
    recursive_mutex_t mutex;
} SyncData;

using recursive_mutex_t = recursive_mutex_tt<DEBUG>;

class recursive_mutex_tt : nocopy_t {
    pthread_mutex_t mLock;
    ...
}

所以@synchronized(self)最终的效果如下:

{
    pthread_mutex_t *self_mutex = LOOK_UP_MUTEX(self);
    pthread_mutex_lock(self_mutex);
    // code
    pthread_mutex_unlock(self_mutex);
}

递归锁也意味着如下代码没有问题:

@synchronized (obj) {
    NSLog(@"1st sync");
    @synchronized (obj) {
        NSLog(@"2nd sync");
    }
}

小结

  • 你调用 sychronized 的每个对象,Objective-C runtime 都会为其分配一个递归锁并存储在哈希表中。
  • 如果在 sychronized 内部对象被释放或被设为 nil 看起来都 OK。不过这没在文档中说明,所以我不会再生产代码中依赖这条。
  • 注意不要向你的 sychronized block 传入 nil!这将会从代码中移走线程安全。你可以通过在 objc_sync_nil 上加断点来查看是否发生了这样的事情。

参考

相关文章

网友评论

      本文标题:从atomic说起

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