- 在 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元,然后开启两个子线程,一个子线程存钱,另一个子线程取钱;
- 分别执行两个场景的代码,得到如下结果:
- 下面通过线程锁来解决上述的线程安全问题;
- 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_63.png-4e7ac6-1632649343519-0)]
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
就是所谓的递归锁
-
递归锁
允许同一个线程
对同一把锁
进行重复加锁,注意⚠️两个同一
,在otherTest1
和otherTest2
函数中都是主线程对_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锁是否被加锁,若已加过就会进入休眠状态
,汇编分析如下:
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
普通锁的封装;
#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
递归锁的封装;
#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_t
和pthread_cond_t
的封装;
#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
的进一步封装,其可以设置具体的条件值;
#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