美文网首页
iOS底层系列25 -- 线程锁

iOS底层系列25 -- 线程锁

作者: YanZi_33 | 来源:发表于2021-09-26 17:09 被阅读0次
    • iOS底层系列22 -- 多线程基础概念 这篇文章中提到线程安全问题,可以通过线程锁来解决线程安全问题,下面给出卖票存取款这两个案例来模拟线程安全问题;
    • 卖票代码如下:
    //卖票测试
    - (void)ticketsTest{
        self.ticketCount = 15;
        
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; 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 oldTicketCount = self.ticketCount;
        sleep(0.2);
        oldTicketCount--;
        self.ticketCount = oldTicketCount;
        
        NSLog(@"还剩%d张票 -- %@",oldTicketCount,[NSThread currentThread]);
    }
    
    • 票数初始为15张,然后开启三个子线程开始卖票;
    • 存取款代码如下:
    //存取款测试
    - (void)moneyTest{
        self.money = 100;
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self saveMoney];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self drawMoney];
            }
        });
    }
    
    //存款
    - (void)saveMoney{
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney+=50;
        self.money = oldMoney;
        
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
    }
    
    //取款
    - (void)drawMoney{
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney-=30;
        self.money = oldMoney;
        
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
    }
    
    • 初始化金额为100元,然后开启两个子线程,一个子线程存钱,另一个子线程取钱;
    • 分别执行两个场景的代码,得到如下结果:
    image.png image.png
    • 下面通过线程锁来解决上述的线程安全问题;
    • iOS中常见的线程锁有如下:
      • OSSpinLock
      • os_unfair_lock
      • pthread_mutex_t
      • NSLock
      • NSRecursiveLock
      • NSCondition
      • NSConditionLock
      • dispatch_queue
      • dispatch_semaphore
      • @synchronized

    OSSpinLock

    • OSSpinLock叫做自旋锁,等待锁的线程会处于忙等状态,一直占用着CPU资源;
    • 需要导入头文件#import <libkern/OSAtomic.h>
    • 利用OSSpinLock,解决线程安全的代码如下:
    #import "ViewController.h"
    #import <libkern/OSAtomic.h>
    
    @interface ViewController ()
    //金额
    @property(nonatomic,assign)int money;
    //票数
    @property(nonatomic,assign)int ticketCount;
    //线程锁
    @property(nonatomic,assign)OSSpinLock lock;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.lock = OS_SPINLOCK_INIT;
        //卖票与存取款 每次执行时,两者只运行其中一个
    //    [self ticketsTest];
        [self moneyTest];
    }
    
    //存取款测试
    - (void)moneyTest{
        self.money = 100;
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self saveMoney];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self drawMoney];
            }
        });
    }
    
    //存款
    - (void)saveMoney{
        //加锁
        OSSpinLockLock(&_lock);
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney+=50;
        self.money = oldMoney;
        
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        //解锁
        OSSpinLockUnlock(&_lock);
    }
    
    //取款
    - (void)drawMoney{
        //加锁
        OSSpinLockLock(&_lock);
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney-=30;
        self.money = oldMoney;
        
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        //解锁
        OSSpinLockUnlock(&_lock);
    }
    
    //卖票测试
    - (void)ticketsTest{
        self.ticketCount = 15;
        
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; 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{
        //加锁
        OSSpinLockLock(&_lock);
        
        int oldTicketCount = self.ticketCount;
        sleep(0.2);
        oldTicketCount--;
        self.ticketCount = oldTicketCount;
        
        NSLog(@"还剩%d张票 -- %@",oldTicketCount,[NSThread currentThread]);
        
        //解锁
        OSSpinLockUnlock(&_lock);
    }
    
    @end
    
    • OSSpinLock目前已经不再安全,可能会出现优先级反转的问题,即如果等待锁的线程优先级比较高,那么它会一直占用着CPU资源,导致优先级低的线程无法释放锁;
    • 假设现在有两条线程,分别为线程一,其优先级较高,线程二,其优先级较低,若线程二首先获取到锁,然后准备执行线程二中逻辑,此时线程一会处于循环忙等状态,但由于其优先级比较高,那么系统会分配更多的时间给线程一,其会一直占用CPU资源,导致线程二的逻辑执行不了,且无法释放锁,这就出现了线程二虽然获取到锁,却执行不了逻辑且无法释放锁
    • 为了探索OSSpinLock的底层实现,将代码做如下改动:
    #import "ViewController.h"
    #import <pthread.h>
    #import <libkern/OSAtomic.h>
    
    @interface ViewController ()
    //金额
    @property(nonatomic,assign)int money;
    //票数
    @property(nonatomic,assign)int ticketCount;
    //线程锁
    //@property(nonatomic,assign)pthread_mutex_t lock;
    
    @property(nonatomic,assign)OSSpinLock lock;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //初始化锁
        self.lock = OS_SPINLOCK_INIT;
     
        [self ticketsTest];
    }
    
    //卖票测试
    - (void)ticketsTest{
        self.ticketCount = 15;
        
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        //开辟10条子线程,开始卖票
        for (int i = 0; i < 10; i++) {
            [[[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil] start];
        }
    }
    
    //卖票
    - (void)saleTicket{
        //加锁
        OSSpinLockLock(&_lock);
        
        int oldTicketCount = self.ticketCount;
        sleep(600);
        oldTicketCount--;
        self.ticketCount = oldTicketCount;
        
        NSLog(@"还剩%d张票 -- %@",oldTicketCount,[NSThread currentThread]);
        
        //解锁
        OSSpinLockUnlock(&_lock);
    }
    
    @end
    
    • saleTicket函数中加断点,当第一条线程执行saleTicket函数断住时,过掉断点,当第二条线程执行到saleTicket函数时断住,此时第二条线程会检测_lock锁是否被加锁,若已加过就会进入自旋忙等状态,汇编分析如下:
      Snip20210926_61.png
    [图片上传中...(Snip20210926_63.png-4e7ac6-1632649343519-0)] Snip20210926_63.png

    os_unfair_lock

    • os_unfair_lock是用于取代不安全的OSSpinLock,从iOS10才开始支持;
    • 从底层调用来看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等;
    • 需要导入头文件#import <os/lock.h>
    • 使用os_unfair_lock的代码如下所示:
    #import "ViewController.h"
    #import <os/lock.h>
    
    @interface ViewController ()
    //金额
    @property(nonatomic,assign)int money;
    //票数
    @property(nonatomic,assign)int ticketCount;
    //线程锁
    @property(nonatomic,assign)os_unfair_lock lock;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.lock = OS_UNFAIR_LOCK_INIT;
        //卖票与存取款 每次执行时,两者只运行其中一个
        [self ticketsTest];
    //    [self moneyTest];
    }
    
    //存取款测试
    - (void)moneyTest{
        self.money = 100;
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self saveMoney];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self drawMoney];
            }
        });
    }
    
    //存款
    - (void)saveMoney{
        //加锁
        os_unfair_lock_lock(&_lock);
        
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney+=50;
        self.money = oldMoney;
        
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        //解锁
        os_unfair_lock_unlock(&_lock);
    }
    
    //取款
    - (void)drawMoney{
        //加锁
        os_unfair_lock_lock(&_lock);
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney-=30;
        self.money = oldMoney;
        
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        //解锁
        os_unfair_lock_unlock(&_lock);
    }
    
    //卖票测试
    - (void)ticketsTest{
        self.ticketCount = 15;
        
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; 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{
        //加锁
        os_unfair_lock_lock(&_lock);
        
        int oldTicketCount = self.ticketCount;
        sleep(0.2);
        oldTicketCount--;
        self.ticketCount = oldTicketCount;
        
        NSLog(@"还剩%d张票 -- %@",oldTicketCount,[NSThread currentThread]);
        
        //解锁
        os_unfair_lock_unlock(&_lock);
    }
    
    @end
    

    pthread_mutex_t

    • pthread_mutex叫做互斥锁,等待锁的线程会处于休眠状态
    • 需要导入头文件#import <pthread.h>
    • 使用pthread_mutex的代码如下:
    #import "ViewController.h"
    #import <pthread.h>
    
    @interface ViewController ()
    //金额
    @property(nonatomic,assign)int money;
    //票数
    @property(nonatomic,assign)int ticketCount;
    //线程锁
    @property(nonatomic,assign)pthread_mutex_t lock;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //属性初始化
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
        //锁的初始化
        pthread_mutex_init(&_lock, &attr);
        //属性销毁
        pthread_mutexattr_destroy(&attr);
        
        //卖票与存取款 每次执行时,两者只运行其中一个
    //    [self ticketsTest];
        [self moneyTest];
    }
    
    //锁的销毁
    - (void)dealloc{
        pthread_mutex_destroy(&_lock);
    }
    
    //存取款测试
    - (void)moneyTest{
        self.money = 100;
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self saveMoney];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self drawMoney];
            }
        });
    }
    
    //存款
    - (void)saveMoney{
        //加锁
        pthread_mutex_lock(&_lock);
        
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney+=50;
        self.money = oldMoney;
        
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        //解锁
        pthread_mutex_unlock(&_lock);
    }
    
    //取款
    - (void)drawMoney{
        //加锁
        pthread_mutex_lock(&_lock);
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney-=30;
        self.money = oldMoney;
        
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        //解锁
        pthread_mutex_unlock(&_lock);
    }
    
    //卖票测试
    - (void)ticketsTest{
        self.ticketCount = 15;
        
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; 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{
        //加锁
        pthread_mutex_lock(&_lock);
        
        int oldTicketCount = self.ticketCount;
        sleep(0.2);
        oldTicketCount--;
        self.ticketCount = oldTicketCount;
        
        NSLog(@"还剩%d张票 -- %@",oldTicketCount,[NSThread currentThread]);
        
        //解锁
        pthread_mutex_unlock(&_lock);
    }
    
    @end
    
    • pthread_mutex_t递归锁,实现同一线程可给同一把锁重复加锁
    #import "ViewController.h"
    #import <pthread.h>
    
    @interface ViewController ()
    //金额
    @property(nonatomic,assign)int money;
    //票数
    @property(nonatomic,assign)int ticketCount;
    //线程锁
    @property(nonatomic,assign)pthread_mutex_t lock;
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        //属性初始化
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
        //锁的初始化
        pthread_mutex_init(&_lock, &attr);
        //属性销毁
        pthread_mutexattr_destroy(&attr);
        
        [self otherTest1];
    }
    
    - (void)dealloc{
        pthread_mutex_destroy(&_lock);
    }
    
    - (void)otherTest1{
        //加锁
        pthread_mutex_lock(&_lock);
        NSLog(@"%s",__func__);
        
        [self otherTest2];
        
        //解锁
        pthread_mutex_unlock(&_lock);
    }
    
    - (void)otherTest2{
        //加锁
        pthread_mutex_lock(&_lock);
        
        NSLog(@"%s",__func__);
        
        //解锁
        pthread_mutex_unlock(&_lock);
    }
    
    @end
    
    • 出现死锁,函数otherTest1中执行加锁,然后执行到otherTest2函数,由于_lock锁已经被加过了,所以主线程会等待_lock锁而处于休眠状态,也就是说[self otherTest2]必须等待pthread_mutex_unlock(&_lock) 执行完才会执行,而pthread_mutex_unlock(&_lock)也必须等待[self otherTest2]执行完才会执行,两者相互等待,造成死锁;
    • 解决方案:修改pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) 其中参数PTHREAD_MUTEX_RECURSIVE就是所谓的递归锁
    • 递归锁允许同一个线程同一把锁进行重复加锁,注意⚠️两个同一,在otherTest1otherTest2函数中都是主线程对_lock进行加锁,不会造成主线程的休眠阻塞,所以不会出现死锁;
    • 为了探索pthread_mutex互斥锁的底层实现,将代码做如下改动:
    
    #import "ViewController.h"
    #import <pthread.h>
    
    @interface ViewController ()
    //金额
    @property(nonatomic,assign)int money;
    //票数
    @property(nonatomic,assign)int ticketCount;
    //线程锁
    @property(nonatomic,assign)pthread_mutex_t lock;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //属性初始化
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        //锁的初始化
        pthread_mutex_init(&_lock, &attr);
        //属性销毁
        pthread_mutexattr_destroy(&attr);
        
        //卖票与存取款 每次执行时,两者只运行其中一个
        [self ticketsTest];
    }
    
    - (void)dealloc{
        pthread_mutex_destroy(&_lock);
    }
    
    //卖票测试
    - (void)ticketsTest{
        self.ticketCount = 15;
        for (int i = 0; i < 10; i++) {
            [[[NSThread alloc]initWithTarget:self selector:@selector(saleTicket) object:nil] start];
        }
    }
    
    //卖票
    - (void)saleTicket{
        //加锁
        pthread_mutex_lock(&_lock);
        
        int oldTicketCount = self.ticketCount;
        sleep(600);
        oldTicketCount--;
        self.ticketCount = oldTicketCount;
        
        NSLog(@"还剩%d张票 -- %@",oldTicketCount,[NSThread currentThread]);
        
        //解锁
        pthread_mutex_unlock(&_lock);
    }
    
    @end
    
    • saleTicket函数中加断点,当第一条线程执行saleTicket函数断住时,过掉断点,当第二条线程执行到saleTicket函数时断住,此时第二条线程会检测_lock锁是否被加锁,若已加过就会进入休眠状态,汇编分析如下:
      image.png
    image.png
    • 会执行如下汇编:
    pthread_mutex_lock_init_slow
    pthread_mutex_firstfit_lock_wait
    psynch_mutexwait
    syscall
    
    • 最终当前子线程会进入休眠状态;
    • 也就是说互斥锁的当前子线程进入休眠状态时,最终会调用系统的syscall函数方法;
    • pthread_mutex_t可实现条件锁,见如下代码:
    #import "ViewController.h"
    #import <pthread.h>
    #import <libkern/OSAtomic.h>
    
    @interface ViewController ()
    //线程锁
    @property(nonatomic,assign)pthread_mutex_t lock;
    //条件
    @property(nonatomic,assign)pthread_cond_t cond;
    //数据源
    @property(nonatomic,strong)NSMutableArray *dataArr;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //属性初始化
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        //锁的初始化
        pthread_mutex_init(&_lock, &attr);
        //属性销毁
        pthread_mutexattr_destroy(&attr);
        //条件初始化
        pthread_cond_init(&_cond, NULL);
        
        self.dataArr = [NSMutableArray array];
        
        [self otherTest];
    }
    
    - (void)dealloc{
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_cond);
    }
    
    - (void)otherTest{
        //开辟两条子线程 分别添加与删除 数组元素
        [[[NSThread alloc]initWithTarget:self selector:@selector(removeElement) object:nil] start];
        [[[NSThread alloc]initWithTarget:self selector:@selector(addElement) object:nil] start];
    }
    
    //线程1 删除元素
    - (void)removeElement{
        //加锁
        pthread_mutex_lock(&_lock);
        
        if (self.dataArr.count == 0) {
            //1.线程1进入休眠
            //2.放开锁 唤醒线程2
            //3.线程1设置自己被唤醒的条件_cond
            //4.线程1被唤醒之后 会再次加锁 然后执行下面的罗
            pthread_cond_wait(&_cond, &_lock);
        }
        
        [self.dataArr removeLastObject];
        NSLog(@"删除了元素");
        
        //解锁
        pthread_mutex_unlock(&_lock);
    }
    
    //线程2 添加元素
    - (void)addElement{
        //加锁
        pthread_mutex_lock(&_lock);
        
        [self.dataArr addObject:@"test"];
        NSLog(@"添加了元素");
        //唤醒线程1
        pthread_cond_signal(&_cond);
        NSLog(@"-----");
        //解锁
        pthread_mutex_unlock(&_lock);
    }
    @end
    
    • 若线程1删除元素首先获取到锁_lock,那么线程2添加元素会阻塞进入休眠状态,然后线程1发现数组元素为空,执行pthread_cond_wait(&_cond, &_lock),此行代码包含四层含义:
      • 1.线程1阻塞进入休眠状态;
      • 2.线程1释放锁_lock,那么线程二获取到锁(加锁),然后开始执行自己的逻辑;
      • 3.线程1会设置自己被唤醒重新执行的条件_cond
      • 4.当线程1被唤醒时,会重新再次加锁;
    • 线程2获取到锁(加锁),然后执行添加元素,然后执行pthread_cond_signal(&_cond)发信号给线程1,接着往下执行释放锁,然后线程1收到信号,解除休眠,并再次加锁,然后执行删除元素,最后解锁;
    • 上述的条件锁的执行逻辑,实现了两个子线程之间的相互通信以及线程之间的依赖逻辑

    NSLock

    • NSLock是对pthread_mutex_t普通锁的封装;
      image.png
    #import "ViewController.h"
    
    @interface ViewController ()
    //金额
    @property(nonatomic,assign)int money;
    //票数
    @property(nonatomic,assign)int ticketCount;
    //线程锁
    @property(nonatomic,strong)NSLock *lock;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.lock = [[NSLock alloc]init];
        
        //卖票与存取款 每次执行时,两者只运行其中一个
        [self ticketsTest];
    //    [self moneyTest];
    }
    
    //卖票测试
    - (void)ticketsTest{
        self.ticketCount = 15;
        
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; 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{
        //加锁
        [self.lock lock];
        
        int oldTicketCount = self.ticketCount;
        sleep(0.2);
        oldTicketCount--;
        self.ticketCount = oldTicketCount;
        
        NSLog(@"还剩%d张票 -- %@",oldTicketCount,[NSThread currentThread]);
        
        //解锁
        [self.lock unlock];
    }
    
    //存取款测试
    - (void)moneyTest{
        self.money = 100;
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self saveMoney];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self drawMoney];
            }
        });
    }
    
    //存款
    - (void)saveMoney{
        //加锁
        [self.lock lock];
        
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney+=50;
        self.money = oldMoney;
        
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        //解锁
        [self.lock unlock];
    }
    
    //取款
    - (void)drawMoney{
        //加锁
        [self.lock lock];
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney-=30;
        self.money = oldMoney;
        
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        //解锁
        [self.lock unlock];
    }
    @end
    

    NSRecursiveLock

    • NSRecursiveLock是对pthread_mutex_t递归锁的封装;
      image.png
    #import "ViewController.h"
    
    @interface ViewController ()
    //金额
    @property(nonatomic,assign)int money;
    //票数
    @property(nonatomic,assign)int ticketCount;
    //线程锁
    @property(nonatomic,strong)NSRecursiveLock *lock;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.lock = [[NSRecursiveLock alloc]init];
        
        //卖票与存取款 每次执行时,两者只运行其中一个
        [self ticketsTest];
    //    [self moneyTest];
    }
    
    
    //卖票测试
    - (void)ticketsTest{
        self.ticketCount = 15;
        
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; 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{
        //加锁
        [self.lock lock];
        
        int oldTicketCount = self.ticketCount;
        sleep(0.2);
        oldTicketCount--;
        self.ticketCount = oldTicketCount;
        
        NSLog(@"还剩%d张票 -- %@",oldTicketCount,[NSThread currentThread]);
        
        //解锁
        [self.lock unlock];
    }
    
    //存取款测试
    - (void)moneyTest{
        self.money = 100;
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self saveMoney];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self drawMoney];
            }
        });
    }
    
    //存款
    - (void)saveMoney{
        //加锁
        [self.lock lock];
        
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney+=50;
        self.money = oldMoney;
        
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        //解锁
        [self.lock unlock];
    }
    
    //取款
    - (void)drawMoney{
        //加锁
        [self.lock lock];
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney-=30;
        self.money = oldMoney;
        
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        //解锁
        [self.lock unlock];
    }
    @end
    

    NSCondition

    • NSCondition是对pthread_mutex_tpthread_cond_t的封装;
      image.png
    #import "ViewController.h"
    
    @interface ViewController ()
    //金额
    @property(nonatomic,assign)int money;
    //票数
    @property(nonatomic,assign)int ticketCount;
    //线程锁
    @property(nonatomic,strong)NSCondition *lock;
    //数据源
    @property(nonatomic,strong)NSMutableArray *dataArr;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.lock = [[NSCondition alloc]init];
        
        self.dataArr = [[NSMutableArray array]init];
        [self otherTest];
    }
    
    - (void)otherTest{
        //开辟两条子线程 分别添加与删除 数组元素
        [[[NSThread alloc]initWithTarget:self selector:@selector(removeElement) object:nil] start];
        [[[NSThread alloc]initWithTarget:self selector:@selector(addElement) object:nil] start];
    }
    
    //线程1 删除元素
    - (void)removeElement{
        //加锁
        [self.lock lock];
    
        if (self.dataArr.count == 0) {
            //1.线程1进入休眠
            //2.放开锁 唤醒线程2
            //3.线程1设置自己被唤醒的条件_cond
            //4.线程1被唤醒之后 会再次加锁 然后执行下面的罗
            [self.lock wait];
        }
    
        [self.dataArr removeLastObject];
        NSLog(@"删除了元素");
    
        //解锁
        [self.lock unlock];;
    }
    
    //线程2 添加元素
    - (void)addElement{
        //加锁
        [self.lock lock];
    
        [self.dataArr addObject:@"test"];
        NSLog(@"添加了元素");
        //唤醒线程1
        [self.lock signal];
        
        //解锁
        [self.lock unlock];
    }
    @end
    

    NSConditionLock

    • NSConditionLock是对NSCondition的进一步封装,其可以设置具体的条件值;
      image.png
    #import "ViewController.h"
    
    @interface ViewController ()
    //金额
    @property(nonatomic,assign)int money;
    //票数
    @property(nonatomic,assign)int ticketCount;
    //线程锁
    @property(nonatomic,strong)NSConditionLock *lock;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.lock = [[NSConditionLock alloc]initWithCondition:1];
    }
        
    - (void)otherTest{
        //开辟两条子线程 分别添加与删除 数组元素
        [[[NSThread alloc]initWithTarget:self selector:@selector(one) object:nil] start];
        [[[NSThread alloc]initWithTarget:self selector:@selector(two) object:nil] start];
        [[[NSThread alloc]initWithTarget:self selector:@selector(three) object:nil] start];
    }
    
    //线程1
    - (void)one{
        //加锁
        [self.lock lockWhenCondition:1];
        
        NSLog(@"one");
    
        //1.解锁
        //2.将锁内部的condition条件值置为2
        [self.lock unlockWithCondition:2];
    }
    
    //线程2
    - (void)two{
        //加锁
        [self.lock lockWhenCondition:2];
        
        NSLog(@"two");
        
        //1.解锁
        //2.将锁内部的condition条件值置为3
        [self.lock unlockWithCondition:3];
    }
    
    //线程3
    - (void)three{
        //加锁
        [self.lock lockWhenCondition:3];
        
        NSLog(@"three");
        
        //解锁
        [self.lock unlock];
    }
    
    @end
    
    • NSConditionLock初始化的时候,内部的条件值设置为1,也就是说当条件为1时,子线程才会获取锁,执行子线程的内部逻辑;
    • [self.lock unlockWithCondition:2],释放锁,且将锁内部的条件值设置为2;
    • NSConditionLock实现了3条子线程的顺序执行;

    dispatch_queue

    #import "ViewController.h"
    
    @interface ViewController ()
    //金额
    @property(nonatomic,assign)int money;
    //票数
    @property(nonatomic,assign)int ticketCount;
    //串行队列
    @property(nonatomic,strong)dispatch_queue_t queue;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //串行队列
        self.queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
        
        //卖票与存取款 每次执行时,两者只运行其中一个
    //    [self ticketsTest];
        [self moneyTest];
    }
    
    //卖票测试
    - (void)ticketsTest{
        self.ticketCount = 15;
        
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; 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{
        dispatch_sync(self.queue, ^{
            int oldTicketCount = self.ticketCount;
            sleep(0.2);
            oldTicketCount--;
            self.ticketCount = oldTicketCount;
            NSLog(@"还剩%d张票 -- %@",oldTicketCount,[NSThread currentThread]);
        });
    }
    
    //存取款测试
    - (void)moneyTest{
        self.money = 100;
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self saveMoney];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self drawMoney];
            }
        });
    }
    
    //存款
    - (void)saveMoney{
        dispatch_sync(self.queue, ^{
            int oldMoney = self.money;
            sleep(0.2);
            oldMoney+=50;
            self.money = oldMoney;
            NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        });
    }
    
    //取款
    - (void)drawMoney{
        dispatch_sync(self.queue, ^{
            int oldMoney = self.money;
            sleep(0.2);
            oldMoney-=30;
            self.money = oldMoney;
            NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        });
    }
    @end
    
    • 将所有的任务全部放入串行队列中,然后任务依次执行;

    dispatch_semaphore

    • dispatch_semaphore是信号量,其可以控制多线程的并发量;
    #import "ViewController.h"
    
    @interface ViewController ()
    //金额
    @property(nonatomic,assign)int money;
    //票数
    @property(nonatomic,assign)int ticketCount;
    //信号量
    @property(nonatomic,strong)dispatch_semaphore_t semaphore;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.lock = [[NSConditionLock alloc]initWithCondition:1];
        //串行队列
        self.queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
        
        self.semaphore = dispatch_semaphore_create(1);
        
        //卖票与存取款 每次执行时,两者只运行其中一个
    //    [self ticketsTest];
        [self moneyTest];
    }
    
    
    //卖票测试
    - (void)ticketsTest{
        self.ticketCount = 15;
        
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; 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{
        //信号量值-1
        //若信号量值>0,继续执行
        //若信号量值<=0,当前子线程阻塞休眠
        dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
        
        int oldTicketCount = self.ticketCount;
        sleep(0.2);
        oldTicketCount--;
        self.ticketCount = oldTicketCount;
        NSLog(@"还剩%d张票 -- %@",oldTicketCount,[NSThread currentThread]);
        //信号量值+1
        dispatch_semaphore_signal(self.semaphore);
    }
    
    //存取款测试
    - (void)moneyTest{
        self.money = 100;
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self saveMoney];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self drawMoney];
            }
        });
    }
    
    //存款
    - (void)saveMoney{
        //信号量值-1
        //若信号量值>0,继续执行
        //若信号量值<=0,当前子线程阻塞休眠
        dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
        
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney+=50;
        self.money = oldMoney;
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        
        //信号量值+1
        dispatch_semaphore_signal(self.semaphore);
    }
    
    //取款
    - (void)drawMoney{
        //信号量值-1
        //若信号量值>0,继续执行
        //若信号量值<=0,当前子线程阻塞休眠
        dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
        
        int oldMoney = self.money;
        sleep(0.2);
        oldMoney-=30;
        self.money = oldMoney;
        NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
    
        //信号量值+1
        dispatch_semaphore_signal(self.semaphore);
    }
    @end
    
    • dispatch_semaphore_t信号量的初始值为1,保证同一时间只有一条线程在执行任务,就能保证线程安全问题;

    @synchronized

    • @synchronized是对pthread_mutex_t递归锁的封装,支持递归锁;
    #import "ViewController.h"
    
    @interface ViewController ()
    //金额
    @property(nonatomic,assign)int money;
    //票数
    @property(nonatomic,assign)int ticketCount;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        //串行队列
        self.queue = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
        
        self.semaphore = dispatch_semaphore_create(1);
        
        //卖票与存取款 每次执行时,两者只运行其中一个
        [self ticketsTest];
    //    [self moneyTest];
    }
    
    
    //卖票测试
    - (void)ticketsTest{
        self.ticketCount = 15;
        
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; 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{
        @synchronized (self) {
            int oldTicketCount = self.ticketCount;
            sleep(0.2);
            oldTicketCount--;
            self.ticketCount = oldTicketCount;
            NSLog(@"还剩%d张票 -- %@",oldTicketCount,[NSThread currentThread]);
        }
    }
    
    //存取款测试
    - (void)moneyTest{
        self.money = 100;
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self saveMoney];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 10; i++) {
                [self drawMoney];
            }
        });
    }
    
    //存款
    - (void)saveMoney{
        @synchronized (self) {
            int oldMoney = self.money;
            sleep(0.2);
            oldMoney+=50;
            self.money = oldMoney;
            NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        }
    }
    
    //取款
    - (void)drawMoney{
        @synchronized (self) {
            int oldMoney = self.money;
            sleep(0.2);
            oldMoney-=30;
            self.money = oldMoney;
            NSLog(@"还剩%d元 -- %@",oldMoney,[NSThread currentThread]);
        }
    }
    @end
    
    • 参数self与锁对象是以key-value的形式存储在底层的哈希表中;
    线程锁的性能比较
    • 性能从高到低的顺序如下所示:
      • os_unfair_lock
      • OSSpinLock
      • dispatch_semaphore
      • pthread_mutex_t
      • dispatch_queue(串行队列)
      • NSLock
      • NSCondition
      • NSRecursiveLock
      • NSConditionLock
      • @synchronized

    相关文章

      网友评论

          本文标题:iOS底层系列25 -- 线程锁

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