zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    pl2303 驱动
    tomcat 启动脚本
    Linux下Shell命令加减乘除计算
    定时删除文件夹"$1"下最后修改时间大于当前时间"$2"天的文件
    mysql 拼接字符
    jquery iframe父子框架中的元素访问方法
    在线工具
    js对数组对象的操作以及方法的使用
    HTML 设置字体
    10月1号 备忘录
  • 原文地址:https://www.cnblogs.com/yangwenhuan/p/9193840.html
Copyright © 2011-2022 走看看