美文网首页
iOS 锁 部分三

iOS 锁 部分三

作者: 飞不越疯人院 | 来源:发表于2020-08-02 09:54 被阅读0次

    主要讲解锁@synchronized/dispatch_semaphore_t的基本用法

    文中使用的 objc4源码是objc4-781和 objc4-723版本;
    文中使用的 libplatform 源码版本为libplatform-177.270.1版本

    常见锁的分类:

    • 自旋锁OSSpinLock
    • 互斥锁os_unfair_lock
    • 互斥/递归/条件锁pthread_mutex_t
    • 互斥锁NSLock
    • 递归锁NSRecursiveLock
    • 条件锁NSCondition
    • 条件锁NSConditionLock
    • 递归锁 @synchronized
    • 信号量semaphore
    • 读写锁pthread_rwlock_t
    • 异步栅栏dispatch_barrier_async

    iOS 锁 部分一
    iOS 锁 部分二
    iOS 锁 部分三
    iOS 锁 部分四


    1. 递归锁@synchronized

    特点

    1. 网上很多教程都说@synchronized是一个基于pthread_mutex_t封装的递归锁, 但是这是在objc4-750版本之前(iOS12之前)是这样, 验证步骤详见补充部分; 从objc4-750版本开始, 以后的(iOS12之后)这个锁的实现已经变了, 底层的封装变为了os_unfair_lock;下面来验证它
      我们加一个断点后看下他的汇编调用过程, 最终可以确定@synchronized调用了两个方法objc_sync_enterobjc_sync_exit;


      下面去 objc源码中查看下这俩方法的实现;
    // Begin synchronizing on 'obj'. 
    // Allocates recursive mutex associated with 'obj' if needed.
    // Returns OBJC_SYNC_SUCCESS once lock is acquired.  
    int objc_sync_enter(id obj)
    {
        int result = OBJC_SYNC_SUCCESS;
        if (obj) {
            ///根据传入的对象, 来获取一个锁. 所以@sycnhronized 时传入的对象很重要;
            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;
    }
    
    ===>
    SyncData的底层结构为
    typedef struct alignas(CacheLineSize) SyncData {
        struct SyncData* nextData;
        DisguisedPtr<objc_object> object;
        int32_t threadCount;  // number of THREADS using this block
        recursive_mutex_t mutex;
    } SyncData;
    
    ===>
    ///recursive_mutex_t的定义为
    using recursive_mutex_t = recursive_mutex_tt<LOCKDEBUG>;
    
    ===>
    ///recursive_mutex_tt的底层结构为
    class recursive_mutex_tt : nocopy_t {
        os_unfair_recursive_lock mLock;
      public:
        constexpr recursive_mutex_tt() : mLock(OS_UNFAIR_RECURSIVE_LOCK_INIT) {
            lockdebug_remember_recursive_mutex(this);
        }
    
        constexpr recursive_mutex_tt(const fork_unsafe_lock_t unsafe)
            : mLock(OS_UNFAIR_RECURSIVE_LOCK_INIT)
        { }
    
        void lock()
    ...
    }
    

    到了os_unfair_recursive_lock这一层 objc4源码就不再有更进一步的结构, 但是我们从libplatform中可以进行进一步的探究


    下载后找到lock_private.h文件
    ///iOS12之后才是这种结构
    #define OS_UNFAIR_RECURSIVE_LOCK_AVAILABILITY \
            __OSX_AVAILABLE(10.14) __IOS_AVAILABLE(12.0) \
            __TVOS_AVAILABLE(12.0) __WATCHOS_AVAILABLE(5.0)
    
    ===>
    /*!
     * @typedef os_unfair_recursive_lock
     *
     * @abstract
     * Low-level lock that allows waiters to block efficiently on contention.
     *
     * @discussion
     * See os_unfair_lock.
     */
    OS_UNFAIR_RECURSIVE_LOCK_AVAILABILITY
    typedef struct os_unfair_recursive_lock_s {
            ///底层为互斥锁os_unfair_lock
        os_unfair_lock ourl_lock;
            ///因为@synchronized 为递归锁, 所以需要记录加锁次数
        uint32_t ourl_count;
    } os_unfair_recursive_lock, *os_unfair_recursive_lock_t;
    

    至此可以验证@synchronized为一个封装了pthread_mutex_t 或者os_unfair_lock的递归锁;

    1. 可能会用到的方法
      传入一个对象obj进行加锁, 如果传入空, 则不执行操作;
      @synchronized(obj) { ... }
    2. 测试代码
    #import "ViewController9.h"
    @interface ViewController9 ()
    @end
    @implementation ViewController9
    - (void)viewDidLoad {
        [super viewDidLoad];
        [self recuresiveAction];
    }
    - (void)recuresiveAction {
        static int count = 10;
        @synchronized ([self class]) {
            NSLog(@"count: %d", count);
            if (count > 0) {
                count --;
                [self recuresiveAction];
            }
        }
    }
    @end
    

    2. 信号量dispatch_semaphore

    特点

    1. 本来使用于控制线程的最大并发数量的, 我们将并发数量设置为1也可以认为是加锁的功能;
    2. 可能会用到的方法
      2.1 初始化dispatch_semaphore_create()传入的值为最大并发数量, 设置为1则达到加锁效果;
      2.2 判断信号量的值dispatch_semaphore_wait()如果大于0, 则可以继续往下执行(同时信号量的值减去一),如果信号量的值为0, 则线程进入休眠状态等待(此方法的第二个参数就是设置要等多久, 一般都是使用永久DISPATCH_TIME_FOREVER) ;
      2.3 释放信号量dispatch_semaphore_signal()同时使信号量的值加上一;
    3. 测试代码
    #import "ViewController10.h"
    @interface ViewController10 ()
    @property (nonatomic, strong) dispatch_semaphore_t semaphore;
    @end
    @implementation ViewController10
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.semaphore = dispatch_semaphore_create(1);
        [self handleMoney];
    }
    - (void)handleMoney {
        self.money = 100;
        __weak typeof(self) weakSelf = self;
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i ++) {
                [weakSelf saveMoney];
            }
        });
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i ++) {
                [weakSelf drawMoney];
            }
        });
    }
    - (void)saveMoney{
        dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
        [super saveMoney];
        dispatch_semaphore_signal(self.semaphore);
    }
    - (void)drawMoney {
        dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
        [super drawMoney ];
        dispatch_semaphore_signal(self.semaphore);
    }
    @end
    


    补充

    1. 在objc4-750之前的@synchronized底层封装的是 pthread_mutex_t

    验证过程, 使用objc4-723版本进行查看

    // Begin synchronizing on 'obj'. 
    // Allocates recursive mutex associated with 'obj' if needed.
    // Returns OBJC_SYNC_SUCCESS once lock is acquired.  
    int objc_sync_enter(id obj)
    {
        int result = OBJC_SYNC_SUCCESS;
        if (obj) {
            ///根据传入的对象, 来获取一个锁. 所以@sycnhronized 时传入的对象很重要;
            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;
    }
    
    ===>
    SyncData的底层结构为
    typedef struct alignas(CacheLineSize) SyncData {
        struct SyncData* nextData;
        DisguisedPtr<objc_object> object;
        int32_t threadCount;  // number of THREADS using this block
        recursive_mutex_t mutex;
    } SyncData;
    
    ===>
    ///recursive_mutex_t的定义为
    using recursive_mutex_t = recursive_mutex_tt<LOCKDEBUG>;
    
    ===>
    ///在 objc4-723版本中recursive_mutex_tt的底层结构为
    class recursive_mutex_tt : nocopy_t {
        ///底层封装的是互斥锁pthread_mutex_t
        pthread_mutex_t mLock;
    
      public:
        recursive_mutex_tt() : mLock(PTHREAD_RECURSIVE_MUTEX_INITIALIZER) {
            lockdebug_remember_recursive_mutex(this);
        }
    
        recursive_mutex_tt(const fork_unsafe_lock_t unsafe)
            : mLock(PTHREAD_RECURSIVE_MUTEX_INITIALIZER)
        { }
    ...
    }
    

    文中测试代码
    参考文章
    objc4源码下载地址
    libplatform源码下载地址

    相关文章

      网友评论

          本文标题:iOS 锁 部分三

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