美文网首页iOS 开发 Objective-C
iOS 底层 day21 多线程锁 os_unfair_lock

iOS 底层 day21 多线程锁 os_unfair_lock

作者: 望穿秋水小作坊 | 来源:发表于2020-09-16 18:03 被阅读0次

    一、 os_unfair_lock

    1. os_unfair_lock 简介
    • os_unfair_lock 用于取代不安全的 OSSpinLock,从 iOS10 开始支持
    • 从底层调用(汇编)看,等待 os_unfair_lock 锁的线程会处于休眠状态,并非忙等
    • 需要导入头文件 #import <os/lock.h>
    2. os_unfair_lock 主要方法介绍
    os_unfair_lock
    3. os_unfair_lock 代码演示
    #import "OSUnFairLockDemo.h"
    #import <os/lock.h>
    
    @interface OSUnFairLockDemo ()
    @property(nonatomic, assign) os_unfair_lock ticketLock;
    @property(nonatomic, assign) os_unfair_lock moneyLock;
    @end
    
    @implementation OSUnFairLockDemo
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            self.ticketLock = OS_UNFAIR_LOCK_INIT;
            self.moneyLock = OS_UNFAIR_LOCK_INIT;
        }
        return self;
    }
    - (void)__saleTicket {
        os_unfair_lock_lock(&_ticketLock);
        [super __saleTicket];
        os_unfair_lock_unlock(&_ticketLock);
    }
    - (void)__saveMoney {
        os_unfair_lock_lock(&_moneyLock);
        [super __saveMoney];
        os_unfair_lock_unlock(&_moneyLock);
    }
    - (void)__drawMoney {
        os_unfair_lock_lock(&_moneyLock);
        [super __drawMoney];
        os_unfair_lock_unlock(&_moneyLock);
    }
    @end
    
    • YYBaseDemo 是封装了前面存取钱卖票例子的代码,用于测试我们加的锁,封装代码我放在最后面。

    二、 pthread_mutex

    1. pthread_mutex 简介
    • mutex 叫做 “互斥锁”,等待锁的线程会处于休眠状态
    • 需要导入头文件 #import <pthread.h>
    • pthread_ 开头的一般是跨平台的
    2. pthread_mutex 主要方法介绍
    pthread_mutex
    3. pthread_mutex 代码演示
    #import "PthreadMutexDemo.h"
    #import <pthread.h>
    
    @interface PthreadMutexDemo ()
    @property(nonatomic, assign)pthread_mutex_t ticketMutex;
    @property(nonatomic, assign)pthread_mutex_t monetMutex;
    @end
    
    @implementation PthreadMutexDemo
    
    - (void) __initMutex:(pthread_mutex_t *)mutex{
        // 初始化锁的属性
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
        pthread_mutex_init(mutex, &attr);
        
        pthread_mutexattr_destroy(&attr);
    }
    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            [self __initMutex:&_ticketMutex];
            [self __initMutex:&_monetMutex];
        }
        return self;
    }
    
    - (void)__saleTicket {
        pthread_mutex_lock(&_ticketMutex);
        [super __saleTicket];
        pthread_mutex_unlock(&_ticketMutex);
    }
    
    - (void)__saveMoney {
        pthread_mutex_lock(&_monetMutex);
        [super __saveMoney];
        pthread_mutex_unlock(&_monetMutex);
    }
    
    - (void)__drawMoney {
        pthread_mutex_lock(&_monetMutex);
        [super __drawMoney];
        pthread_mutex_unlock(&_monetMutex);
    }
    
    - (void)dealloc {
        pthread_mutex_destroy(&_ticketMutex);
        pthread_mutex_destroy(&_monetMutex);
    }
    
    @end
    

    三、 pthread_mutex 递归锁

    1. pthread_mutex 递归锁简介
    • 递归锁:允许同一个线程对同一把锁重复加锁
    2. os_unfair_lock 代码演示
    - (void) __otherFunc {
        pthread_mutex_lock(&_recursiveMutex);
        static int invokeCount = 0;
        
        if (invokeCount < 10) {
            invokeCount++;
            sleep(1.0);
            NSLog(@"%d %@",invokeCount, [NSThread currentThread]);
            [self __otherFunc];
        }
        pthread_mutex_unlock(&_recursiveMutex);
    }
    
    • 如果一个线程想执行上述代码,将会因为自我调用,重复加锁导致线程永远睡眠(死锁)
    • pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 初始化时候,传递这个参数,即可获得一个递归锁。这样就不会导致递归调用的死锁问题了。

    四、 pthread_mutex 添加条件

    1. pthread_mutex 添加条件简介
    • 在多线程执行任务的是时,有时候我们需要保证 任务 1先执行完毕,任务 2才能开始执行,这时候我们就需要给锁添加条件
      pthread_mutex 添加条件
    2. 问题展示
    - (void)__remove {
        [self.mutableArray removeLastObject];
        NSLog(@"__remove:%@",self.mutableArray);
    }
    
    - (void)__add {
        [self.mutableArray addObject:@"11"];
        NSLog(@"__add:%@",self.mutableArray);
    }
    
    
    - (void)otherTest {
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_async(queue, ^{
            [self __remove];
        });
        dispatch_async(queue, ^{
            [self __add];
        });
    }
    
    • 我们需要保证无论哪个线程先调用,最终 mutableArray数组都是空的
    3. pthread_mutex 添加条件代码演示
    #import "PthreadMutexDemo2.h"
    #import <pthread.h>
    
    @interface PthreadMutexDemo2 ()
    @property(nonatomic, assign)pthread_mutex_t mutex;
    @property(nonatomic, assign)pthread_cond_t cond;
    @property(nonatomic, strong)NSMutableArray *mutableArray;
    @end
    
    @implementation PthreadMutexDemo2
    
    - (instancetype)init
    {
        self = [super init];
        if (self) {
            
            // 初始化锁的属性
            pthread_mutexattr_t attr;
            pthread_mutexattr_init(&attr);
            pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
            pthread_mutex_init(&_mutex, &attr);
            
            // 初始化锁的条件
            pthread_cond_init(&_cond, NULL);
            
            self.mutableArray = [NSMutableArray array];
        }
        return self;
    }
    
    
    - (void)__remove {
        pthread_mutex_lock(&_mutex);
        if (self.mutableArray.count == 0) {
            pthread_cond_wait(&_cond, &_mutex);
        }
        [self.mutableArray removeLastObject];
        NSLog(@"__remove:%@",self.mutableArray);
        pthread_mutex_unlock(&_mutex);
    }
    
    - (void)__add {
        pthread_mutex_lock(&_mutex);
        [self.mutableArray addObject:@"11"];
        NSLog(@"__add:%@",self.mutableArray);
    //    pthread_cond_broadcast(&_cond);
        pthread_cond_signal(&_cond);
        pthread_mutex_unlock(&_mutex);
    }
    
    
    - (void)otherTest {
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_async(queue, ^{
            [self __remove];
        });
        dispatch_async(queue, ^{
            sleep(1.0);
            [self __add];
        });
    }
    
    @end
    
    • 这样就保证了无论何时都会先执行 __add 然后再执行 __remove

    五、 NSLock NSCondition

    • NSLock 就是对 un_fair_lock 的 OC 包装
    • NSCondition 就是对 pthread_mutex 的 OC 包装
    • 用法类似,如果需要, todo:后面在添加用法展示
      -todo:用断点追踪一下 NSLock 底层实现

    六、 从汇编角度观察 OSSpinLock 、os_unfair_lock 和 pthread_mutex 等待锁的逻辑

    1. OSSpinLock
    • 打断点,断住第二个(第一个不需要等待锁)来到这句代码的子线程 OSSpinLockLock(&ticketsLock);
    • si 这个 lldb 指令,一直执行,会来到下图这个循环
      OSSpinLock
    • 由汇编效果得知,OSSpinLock 等待锁时,不会进入睡眠,会一直执行一个循环
    2. os_unfair_lock
    • 打断点,断住第二个(第一个不需要等待锁)来到这句代码的子线程 os_unfair_lock_lock(&_ticketLock);

    • si 这个 lldb 指令,一直执行,会来到下图

      os_unfair_lock
    • 我们看到有个 syscall ,表示当前线程调用了系统的函数,再过掉此代码,发现断点消失了。

    • 由此可知 os_unfair_lock ,会调用 syscall 进入休眠状态。

    3. pthread_mutex
    • 打断点,断住第二个(第一个不需要等待锁)来到这句代码的子线程 pthread_mutex_lock(&_ticketMutex);

    • si 这个 lldb 指令,一直执行,会来到下图

      image.png
    • 我们看到有个 syscall ,表示当前线程调用了系统的函数,再过掉此代码,发现断点消失了。

    • 由此可知 pthread_mutex 等待锁的逻辑和 os_unfair_lock 一样,会调用 syscall 进入休眠状态。

    七、 YYBaseDemo 是封装了前面存取钱卖票例子的代码

    #import "YYBaseDemo.h"
    
    @interface YYBaseDemo ()
    @property(nonatomic, assign) int moneyCount;
    @property(nonatomic, assign) int ticketsCount;
    @end
    
    @implementation YYBaseDemo
    
    - (void)moneyTest {
        self.moneyCount = 1000;
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        dispatch_async(queue, ^{
            for (int i = 0 ; i < 10; i++) {
            [self __drawMoney];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0 ; i < 10; i++) {
            [self __saveMoney];
            }
        });
        
    }
    
    - (void)__drawMoney {
        int readCount = self.moneyCount;
        sleep(0.2); //模拟网络拥堵
        int remainCount = readCount - 100;
        self.moneyCount = remainCount;
        NSLog(@"存钱---%@---剩余:%d",[NSThread currentThread], remainCount);
    }
    
    - (void)__saveMoney {
        int readCount = self.moneyCount;
        sleep(0.2); //模拟网络拥堵
        int remainCount = readCount + 200;
        self.moneyCount = remainCount;
        NSLog(@"取钱---%@---剩余:%d",[NSThread currentThread], remainCount);
    }
    
    - (void)ticketTest {
        self.ticketsCount = 20;
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self __saleTicket];
            }
        });
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [self __saleTicket];
            }
        });
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [self __saleTicket];
            }
        });
    }
    
    - (void)__saleTicket {
        int readCount = self.ticketsCount;
        sleep(0.2); //模拟网络拥堵
        int remainCount = readCount - 1;
        self.ticketsCount = remainCount;
        NSLog(@"卖票---%@---剩余:%d",[NSThread currentThread], remainCount);
    }
    
    - (void)otherTest{
        
    }
    @end
    

    相关文章

      网友评论

        本文标题:iOS 底层 day21 多线程锁 os_unfair_lock

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