我们在开发过程中可能会 遇到 多个线程访问 同一块资源,比如:多个线程访问同一个文件,那么多个线程在对这个文件进行读写操作的时候,很容易引发数据错乱和数据安全的问题
解决这个问题,一般采用 同步线程技术,对同一块资源进行加锁和解锁
模拟一个场景,比如卖票
@interface ViewController ()
@property (assign, nonatomic) int ticketsCount;
@end
- (void)viewDidLoad {
[super viewDidLoad];
//卖票
[self ticketTest];
}
- (void)saleTicket
{
int oldTicketsCount = self.ticketsCount;
//线程阻塞0.1秒
sleep(0.1);
//票数 -1
oldTicketsCount--;
//总票数
self.ticketsCount = oldTicketsCount;
NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
}
/**
进行卖票
*/
- (void)ticketTest
{
self.ticketsCount = 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];
}
});
}
调用 上面的代码之后 发现 数据混乱
1.OSSpinLock 自旋锁
需要导入头文件
#import <libkern/OSAtomic.h>
自旋锁其实就是一个等待的过程,因为一直在CPU的资源
这个锁已经废弃了,因为太好性能
注意: 在使用所有的
锁
都是保持,这锁对象是同一把锁对象
1.1用法
- (void)viewDidLoad {
[super viewDidLoad];
//创建一把锁对象
self.lock = OS_SPINLOCK_INIT;
//加锁
OSSpinLockLock(& _lock);
// 要加锁的内容
代码省略。。。
//解锁
OSSpinLockUnlock(& _lock);
}
及上述 卖票的使用使用
@interface ViewController ()
@property (assign, nonatomic) int ticketsCount;
//声明一个锁属性
@property (assign, nonatomic) OSSpinLock lock;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化锁
self.lock = OS_SPINLOCK_INIT;
[self ticketTest];
}
- (void)saleTicket
{
// 加锁
OSSpinLockLock(&_lock);
int oldTicketsCount = self.ticketsCount;
sleep(.2);
oldTicketsCount--;
self.ticketsCount = oldTicketsCount;
NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
// 解锁
OSSpinLockUnlock(&_lock);
}
2.os_unfair_lock锁
os_unfair_lock
取代了OSSpinLock
锁,是iOS10才有的这个API
需要导入 头文件#import <os/lock.h>
特点:等不到锁就休眠
2.1 用法
- (void)viewDidLoad {
[super viewDidLoad];
self.fairLock = OS_UNFAIR_LOCK_INIT;
//加锁
os_unfair_lock_lock(&_fairLock);
//要加锁的代码
//解锁
os_unfair_lock_unlock(&_fairLock);
}
3. pthread_mutex_t 锁
#import <pthread.h>
@interface ViewController ()
//保存锁
@property (assign, nonatomic) pthread_mutex_t mutex;
//保存属性
@property (assign, nonatomic) pthread_mutexattr_t attr;
@end
- (void)viewDidLoad {
[super viewDidLoad];
/**
//普通锁
#define PTHREAD_MUTEX_NORMAL 0
//检查锁
#define PTHREAD_MUTEX_ERRORCHECK 1
//递归锁
#define PTHREAD_MUTEX_RECURSIVE 2
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
*/
//初始化锁的属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
//普通的锁
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
//初始化锁
pthread_mutex_t mutex;
//传入mutex 和 锁的属性 attr
pthread_mutex_init(&mutex, &attr);
//用属性 保存这把锁
self.mutex = mutex;
self.attr = attr;
//记得销毁
}
//控制器销毁时 销毁这个锁和锁的属性
- dealloc {
//销毁
pthread_mutexattr_destroy(&_attr);
pthread_mutex_destroy(&_mutex);
}
- (void)test {
//加锁
pthread_mutex_lock(&_mutex);
//要加锁的代码
//解锁
pthread_mutex_unlock(&_mutex);
}
3.1 pthread_mutex_t 中的条件锁
解释: JCCondPthread类中创建 pthread对象,调用creatThread
创建2个子线程
,然后执行2个 操作数组数据的方法,由于不知道哪个子线程优先执行,所以在创建执行的时候,加了cond
条件
1.如果删除元素优先执行,发现数组里面没有元素,那么在执行pthread_cond_wait(&_cond, &_mutex);
的时候,线程就会去等待状态, 那么addData
在执行的时候,添加数据,然后发出一个激活cond
条件的信号,就会激活removeData
这个线程
#import "JCCondPthread.h"
#import <pthread.h>
@interface JCCondPthread ()
//锁
@property (nonatomic, assign) pthread_mutex_t mutex;
//条件
@property (nonatomic, assign) pthread_cond_t cond;
@property (nonatomic, strong) NSMutableArray *data;
@end
@implementation JCCondPthread
/**
条件锁
*/
- (instancetype)init {
self = [super init];
if (self) {
//初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
//设置属性
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
//创建锁
pthread_mutex_init(&_mutex, &attr);
//销毁属性
pthread_mutexattr_destroy(&attr);
//创建条件
pthread_cond_init(&_cond, NULL);
//创建数据
self.data = [NSMutableArray array];
}
return self;
}
//创建2个子线程
- (void)creatThread {
[[[NSThread alloc] initWithTarget:self selector:@selector(removeData) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(addData) object:nil] start];
}
- (void)removeData {
//加锁
pthread_mutex_lock(&_mutex);
if (self.data.count == 0) {
//如果没有数据,就d等待休眠
pthread_cond_wait(&_cond, &_mutex);
}
//移除元素
[self.data removeLastObject];
NSLog(@"移除元素");
//解锁
pthread_mutex_unlock(&_mutex);
}
-(void)addData{
//解锁
pthread_mutex_lock(&_mutex);
[self.data addObject:@"CC"];
NSLog(@"添加元素");
//激活信号
pthread_cond_signal(&_cond);
//解锁
pthread_mutex_unlock(&_mutex);
}
-(void)dealloc {
//销毁锁
pthread_mutex_destroy(&_mutex);
//销毁条件
pthread_cond_destroy(&_cond);
}
@end
4.NSLock 和 NSRecursiveLock递归锁
NSLock就是对pthread_mutex普通锁的封装
NSRecursiveLock是对pthread_mutex递归锁的封装
- (void)viewDidLoad {
[super viewDidLoad];
self.lock1 = [[NSLock alloc] init];
//加锁
[self.lock1 lock];
//解锁
[self.lock1 unlock];
}
//递归锁
- (void)recuresLock{
//递归锁
self.recuresLock = [[NSRecursiveLock alloc] init];
//加锁
[self.recuresLock lock];
//要执行的代码
//解锁
[self.recuresLock unlock];
}
5.信号量 dispatch_semaphore_t
//初始化 信号量
- (instancetype)init
{
if (self = [super init]) {
//设置最大执行的个数
self.semaphore = dispatch_semaphore_create(5);
}
return self;
}
- (void)test
{
// 如果信号量的值 > 0,就让信号量的值减1,然后继续往下执行代码
// 如果信号量的值 <= 0,就会休眠等待,直到信号量的值变成>0,就让信号量的值减1,然后继续往下执行代码
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
sleep(2);
NSLog(@"test - %@", [NSThread currentThread]);
// 让信号量的值+1
dispatch_semaphore_signal(self.semaphore);
}