OSSpinLock
OSSpinLock叫做“自旋锁”, 等待锁的线程会处于忙等状态
,一直占用着CPU资源。
- 缺点:目前已经不再安全,可能会出现优先级反转问题。
如果等待锁的线程优先级较高,它会一直占用CPU资源,优先级低的线程就无法释放锁。
例如,有两个贤臣:
thread1 (优先级比较高)
thread2 (优先级比较低)
thread2 先用OSSpinLock加锁,此时thread1 进来,一看代码已经被加锁。那么此时,优先级高的线程会一直判断是否加锁;
如果加锁了,就继续一直判断,那么CPU就会一直被优先级高的thread1的线程占用着;
优先级比较低的thread2 的锁就没有时间处理 解锁;然后就会卡死。
- OSSpinLock的常用方法如下:
//导入头文件:#import <libkern/OSAtomic.h>
//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
//尝试加锁(如果需要等待就不加锁,返回false,如果不需要等待就加锁,返回true)
BOOL result = OSSpinLockTry(&lock);
//加锁
OSSpinLockLock(&lock);
//解锁
OSSpinLockUnlock(&lock);
- OSSpinLock的使用demo如下:
#import "ViewController.h"
#import <libkern/OSAtomic.h>
@interface ViewController ()
@property (assign, nonatomic) int money;
@property (assign, nonatomic) int ticketsCount;
@property (assign, nonatomic) OSSpinLock lock;
@property (assign, nonatomic) OSSpinLock lock1;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化锁
self.lock = OS_SPINLOCK_INIT;
self.lock1 = OS_SPINLOCK_INIT;
[self ticketTest];
[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(&_lock1);
int oldMoney = self.money;
sleep(.2);
oldMoney += 50;
self.money = oldMoney;
NSLog(@"存50,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
// 解锁
OSSpinLockUnlock(&_lock1);
}
/**
取钱
*/
- (void)drawMoney
{
// 加锁
OSSpinLockLock(&_lock1);
int oldMoney = self.money;
sleep(.2);
oldMoney -= 20;
self.money = oldMoney;
NSLog(@"取20,还剩%d元 - %@", oldMoney, [NSThread currentThread]);
// 解锁
OSSpinLockUnlock(&_lock1);
}
/**
卖1张票
*/
- (void)saleTicket
{
// 加锁
OSSpinLockLock(&_lock);
int oldTicketsCount = self.ticketsCount;
sleep(.2);
oldTicketsCount--;
self.ticketsCount = oldTicketsCount;
NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
// 解锁
OSSpinLockUnlock(&_lock);
}
/**
卖票演示
*/
- (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];
}
});
}
os_unfair_lock
os_unfair_lock用于取代不安全的OSSpinLock
,从iOS10开始才支持。从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
。
- os_unfair_lock的常用方法如下:
//需要导入头文件#import <os/lock.h>
//初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
//尝试加锁
BOOL result = os_unfair_lock_trylock(&lock);
//加锁
os_unfair_lock_lock(&lock);
//解锁
os_unfair_lock_unlock(&lock);
- os_unfair_lock 使用如下:
- (void)__saleTicket
{
os_unfair_lock_lock(&_ticketLock);
//__saleTicket 父类写了里面的逻辑
[super __saleTicket];
os_unfair_lock_unlock(&_ticketLock);
}
pthread_mutex
mutex叫做”互斥锁”
,等待锁的线程会处于休眠状态
; 需要导入头文件#import <pthread.h>
。
- mutex的使用方法:
// 静态初始化:初始化的一种,一般不推荐
pthread_mutex_t mutex_one = PTHREAD_MUTEX_INITIALIZER;
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
/*
PTHREAD_MUTEX_DEFAULT:表示pthead_mutex 是互斥锁 == PTHREAD_MUTEX_NORMAL
PTHREAD_MUTEX_RECURSIVE:表示是递归所
*/
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化锁
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
//尝试加锁
pthread_mutex_trylock(&mutex);
//加锁
pthread_mutex_lock(&mutex);
//解锁
pthread_mutex_unlock(&mutex);
//销毁锁
pthread_mutex_destroy(&mutex);
注意:
递归锁:允许同一个线程对一把锁进行重复加锁
代码的使用:
#import <pthread.h>
@interface MutexDemo()
@property (assign, nonatomic) pthread_mutex_t ticketMutex;
@property (assign, nonatomic) pthread_mutex_t moneyMutex;
@end
@implementation MutexDemo
- (void)__initMutex:(pthread_mutex_t *)mutex
{
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化锁
pthread_mutex_init(mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
}
- (instancetype)init
{
if (self = [super init]) {
[self __initMutex:&_ticketMutex];
[self __initMutex:&_moneyMutex];
}
return self;
}
// 死锁:永远拿不到锁
- (void)__saleTicket
{
pthread_mutex_lock(&_ticketMutex);
[super __saleTicket];
pthread_mutex_unlock(&_ticketMutex);
}
- (void)__saveMoney
{
pthread_mutex_lock(&_moneyMutex);
[super __saveMoney];
pthread_mutex_unlock(&_moneyMutex);
}
- (void)__drawMoney
{
pthread_mutex_lock(&_moneyMutex);
[super __drawMoney];
pthread_mutex_unlock(&_moneyMutex);
}
- (void)dealloc
{
pthread_mutex_destroy(&_moneyMutex);
pthread_mutex_destroy(&_ticketMutex);
}
@end
pthread_mutex --条件锁
pthread_mutex是可以是互斥锁,也可以是递归所;同时也可以是条件锁。
pthread_mutex作为条件锁的使用如下:
// 初始化属性
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);
//加锁
pthread_mutex_lock(&_mutex);
//等待锁
pthread_cond_wait(&_cond, &_mutex);
//发送条件信号,接触等待
pthread_cond_signal(&_cond);
//解锁
pthread_mutex_unlock(&_mutex);
//销毁锁和销毁条件
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
使用案例如下:
#import <pthread.h>
@interface MutexDemo3()
//锁
@property (assign, nonatomic) pthread_mutex_t mutex;
//条件
@property (assign, nonatomic) pthread_cond_t cond;
@property (strong, nonatomic) NSMutableArray *data;
@end
@implementation MutexDemo3
- (instancetype)init
{
if (self = [super init]) {
// 初始化属性
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;
}
- (void)otherTest
{
[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}
// 生产者-消费者模式
// 线程1
// 删除数组中的元素
- (void)__remove
{
pthread_mutex_lock(&_mutex);
NSLog(@"__remove - begin");
if (self.data.count == 0) {
/*
等待_mutex锁,同时进入休眠的状态;不会执行后面的代码,
直到_cond的条件被唤醒之后,就会对_mutex进行加锁,并执行后面的代码。
*/
pthread_cond_wait(&_cond, &_mutex);
}
[self.data removeLastObject];
NSLog(@"删除了元素");
//解锁
pthread_mutex_unlock(&_mutex);
}
// 线程2
// 往数组中添加元素
- (void)__add
{
//给_mutex加锁
pthread_mutex_lock(&_mutex);
//睡眠1秒
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 给_cond发送信号,唤醒pthread_cond_wait()这个等待的线程,并让他们执行后面的代码
pthread_cond_signal(&_cond);
//解锁
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc
{
//销毁锁和销毁条件
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
@end
NSLock、NSRecursiveLock
- NSLock是对mutex普通锁的封装。NSLock的API如下:
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
@interface NSLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
- NSRecursiveLock也是对
mutex递归锁的封装
,API跟NSLock基本一致。
@interface NSRecursiveLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
以NSLock为例,demo如下:
@interface NSLockDemo()
@property (strong, nonatomic) NSLock *ticketLock;
@property (strong, nonatomic) NSLock *moneyLock;
@end
@implementation NSLockDemo
- (instancetype)init
{
if (self = [super init]) {
self.ticketLock = [[NSLock alloc] init];
self.moneyLock = [[NSLock alloc] init];
}
return self;
}
// 死锁:永远拿不到锁
- (void)__saleTicket
{
[self.ticketLock lock];
[super __saleTicket];
[self.ticketLock unlock];
}
- (void)__saveMoney
{
[self.moneyLock lock];
[super __saveMoney];
[self.moneyLock unlock];
}
- (void)__drawMoney
{
[self.moneyLock lock];
[super __drawMoney];
[self.moneyLock unlock];
}
@end
NSCondition
NSCondition是对mutex和cond的封装。NSCondition的API如下:
@interface NSCondition : NSObject <NSLocking> {
@private
void *_priv;
}
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
NSCondition的使用demo如下:
#import "NSConditionDemo.h"
@interface NSConditionDemo()
@property (strong, nonatomic) NSCondition *condition;
@property (strong, nonatomic) NSMutableArray *data;
@end
@implementation NSConditionDemo
- (instancetype)init
{
if (self = [super init]) {
self.condition = [[NSCondition alloc] init];
self.data = [NSMutableArray array];
}
return self;
}
- (void)otherTest
{
[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}
// 生产者-消费者模式
// 线程1
// 删除数组中的元素
- (void)__remove
{
[self.condition lock];
NSLog(@"__remove - begin");
if (self.data.count == 0) {
// 等待
[self.condition wait];
}
[self.data removeLastObject];
NSLog(@"删除了元素");
[self.condition unlock];
}
// 线程2
// 往数组中添加元素
- (void)__add
{
[self.condition lock];
sleep(1);
[self.data addObject:@"Test"];
NSLog(@"添加了元素");
// 信号
[self.condition signal];
// 广播
// [self.condition broadcast];
[self.condition unlock];
}
@end
NSConditionLock
NSConditionLock
是对NSCondition的进一步封装,可以设置具体的条件值
;从而可以使线程按照一定的顺序执行;NSConditionLock的API如下:
@interface NSConditionLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
demo如下:
@interface NSConditionLockDemo()
@property (strong, nonatomic) NSConditionLock *conditionLock;
@end
@implementation NSConditionLockDemo
- (instancetype)init
{
if (self = [super init]) {
self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];
}
return self;
}
- (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];
}
- (void)__one
{
[self.conditionLock lock];
NSLog(@"__one");
sleep(1);
[self.conditionLock unlockWithCondition:2];
}
- (void)__two
{
[self.conditionLock lockWhenCondition:2];
NSLog(@"__two");
sleep(1);
[self.conditionLock unlockWithCondition:3];
}
- (void)__three
{
[self.conditionLock lockWhenCondition:3];
NSLog(@"__three");
[self.conditionLock unlock];
}
@end
dispatch_semaphore
semaphore叫做”信号量”。信号量的初始值,可以用来控制线程并发访问的最大数量
;
信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步。
信号量的使用:
信号量的demo:
@interface SemaphoreDemo()
@property (strong, nonatomic) dispatch_semaphore_t semaphore;
@property (strong, nonatomic) dispatch_semaphore_t ticketSemaphore;
@property (strong, nonatomic) dispatch_semaphore_t moneySemaphore;
@end
@implementation SemaphoreDemo
- (instancetype)init
{
if (self = [super init]) {
self.semaphore = dispatch_semaphore_create(5);
self.ticketSemaphore = dispatch_semaphore_create(1);
self.moneySemaphore = dispatch_semaphore_create(1);
}
return self;
}
- (void)__drawMoney
{
dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
[super __drawMoney];
dispatch_semaphore_signal(self.moneySemaphore);
}
- (void)__saveMoney
{
dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
[super __saveMoney];
dispatch_semaphore_signal(self.moneySemaphore);
}
- (void)__saleTicket
{
dispatch_semaphore_wait(self.ticketSemaphore, DISPATCH_TIME_FOREVER);
[super __saleTicket];
dispatch_semaphore_signal(self.ticketSemaphore);
}
- (void)otherTest
{
for (int i = 0; i < 20; i++) {
[[[NSThread alloc] initWithTarget:self selector:@selector(test) object:nil] start];
}
}
// 线程10、7、6、9、8
- (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);
}
@end
dispatch_queue
直接使用GCD的串行队列,也是可以实现线程同步的。使用方法如下:
demo如下:
@interface SerialQueueDemo()
@property (strong, nonatomic) dispatch_queue_t ticketQueue;
@property (strong, nonatomic) dispatch_queue_t moneyQueue;
@end
@implementation SerialQueueDemo
- (instancetype)init
{
if (self = [super init]) {
self.ticketQueue = dispatch_queue_create("ticketQueue", DISPATCH_QUEUE_SERIAL);
self.moneyQueue = dispatch_queue_create("moneyQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)__drawMoney
{
dispatch_sync(self.moneyQueue, ^{
[super __drawMoney];
});
}
- (void)__saveMoney
{
dispatch_sync(self.moneyQueue, ^{
[super __saveMoney];
});
}
- (void)__saleTicket
{
dispatch_sync(self.ticketQueue, ^{
[super __saleTicket];
});
}
@synchronized
@synchronized是对mutex递归锁的封装
源码查看:objc4中的objc-sync.mm文件
@synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作
。
使用demo如下:
@implementation SynchronizedDemo
- (void)__drawMoney
{
@synchronized([self class]) {
[super __drawMoney];
}
}
- (void)__saveMoney
{
@synchronized([self class]) { // objc_sync_enter
[super __saveMoney];
} // objc_sync_exit
}
- (void)__saleTicket
{
static NSObject *lock;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
lock = [[NSObject alloc] init];
});
@synchronized(lock) {
[super __saleTicket];
}
}
- (void)otherTest
{
@synchronized([self class]) {
NSLog(@"123");
[self otherTest];
}
}
各个锁性能的比较
自旋锁、互斥锁比较
- 什么情况使用自旋锁比较划算?
- 预计线程等待锁的时间很短
- 加锁的代码(临界区)经常被调用,但竞争情况很少发生
- CPU资源不紧张
- 多核处理器
- 什么情况使用互斥锁比较划算?
- 预计线程等待锁的时间较长
- 单核处理器
- 临界区有IO操作
- 临界区代码复杂或者循环量大
- 临界区竞争非常激烈