Objective-C @synchronized深入理解

@synchronized是线程同步锁,易用、可读性高。

@synchronized(self) {
  临界区
}

利用如下命令将其重写

clang -rewrite-objc file 

得到C++实现

{
        id _sync_obj = (id)self;
        objc_sync_enter(_sync_obj);
        struct _SYNC_EXIT {
            _SYNC_EXIT(id arg) : sync_exit(arg) {}
            ~_SYNC_EXIT() {
                objc_sync_exit(sync_exit);
            }
            id sync_exit;
        } _sync_exit(_sync_obj);
}

深究objc_sync_enter和objc_sync_exit的源码

int objc_sync_enter(id obj)
{
    if (obj) {
        SyncData* data = id2data(obj, ACQUIRE);
        recursive_mutex_lock(&data->mutex); // 获取递归锁
    } else {
        // @synchronized(nil) does nothing
    }
}    
int objc_sync_exit(id obj)
{
    if (obj) {
        SyncData* data = id2data(obj, RELEASE); 
        recursive_mutex_unlock(&data->mutex); // 释放递归锁
    } else {
        // @synchronized(nil) does nothing
    }
}     

可以看到

1、@synchronized底层用的是递归锁,即同个线程可重入,而不会导致死锁。

2、@synchronized(nil)是不会上锁的。

接着,显然SyncList是单链表,SyncData是其中的节点,而整体的存储则是一个“拉链哈希表”。

typedef struct SyncData {
    struct SyncData* nextData; // 指向下一个SyncData节点
    DisguisedPtr<objc_object> object; // @synchronized的参数,即上面的self
    recursive_mutex_t mutex; // 递归锁
} SyncData;

struct SyncList {
    SyncData *data; // 单链表的头指针
    spinlock_t lock; // 自旋锁,性能好,但不绝对安全
    SyncList() : data(nil) { }
};
#define LOCK_FOR_OBJ(obj) sDataLists[obj].lock // 根据参数获取单链表的锁
#define LIST_FOR_OBJ(obj) sDataLists[obj].data // 根据参数获取单链表
static StripedMap<SyncList> sDataLists; // 哈希表
// 根据参数获取对应的SyncData节点,其实就是哈希表查找
static SyncData* id2data(id object, enum usage why)
{
    spinlock_t *lockp = &LOCK_FOR_OBJ(object);
    SyncData **listp = &LIST_FOR_OBJ(object); // 参数对应的单链表
    SyncData* result = NULL;

    lockp->lock();
    {
        SyncData* p;
        // 遍历单链表
        for (p = *listp; p != NULL; p = p->nextData) {
            if ( p->object == object ) {
                // 找到参数对应的SyncData节点
                result = p;
                goto done;
            }
        }
    }
    // 单链表中还没有参数对应的SyncData节点
    result = (SyncData*)calloc(sizeof(SyncData), 1);
    result->object = (objc_object *)object; // 填充数据
    new (&result->mutex) recursive_mutex_t();
   // 新建的SyncData节点往链表头部加
    result->nextData = *listp;
    *listp = result;
 done:
    lockp->unlock();
    return result;
}
template<typename T>
class StripedMap {
        static unsigned int indexForPointer(const void *p) {
            // 取参数地址的哈希值作为数组的index
            uintptr_t addr = reinterpret_cast<uintptr_t>(p);
            return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
        }
    public:
        T& operator[] (const void *p) { 
            return array[indexForPointer(p)].value; 
        }
};   

 

可以看出,参数和递归锁是一一对应的。因此,一定要保证参数不变,这里的不变,指的是完全不变,包括地址。

源码:

https://opensource.apple.com/source/objc4/objc4-680/runtime/objc-sync.mm

原文地址:https://www.cnblogs.com/yangwenhuan/p/9193840.html