iOS底层系列25 -- 线程锁

  • 在 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
  • 利用OSSpinLock,解决线程安全的代码如下:
#import "ViewController.h"
#import 

@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 
#import 

@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_unfair_lock的代码如下所示:
#import "ViewController.h"
#import 

@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_mutex的代码如下:
#import "ViewController.h"
#import 

@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 

@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 

@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 
#import 

@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 -- 线程锁)