多线程的那些锁
2019-05-08 本文已影响7人
coderhlt
一、OSSpinLock
自旋锁: 等待锁的线程会处于忙等(busy-wait)状态,一直占着CPU的资源。(假如有A、B、C三个线程,A第一个获得锁,B、C三个线程将处循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环)
- 目前已经不再安全,可能会出现优先级反转问题。https://blog.csdn.net/feixiaoxing/article/details/7061582
因为线程安全的问题,iOS10之后被Apple废弃,用os_unfair_lock代替。
导入头文件#import <libkern/OSAtomic.h>
// 初始化锁
OSSpinLock lock = OS_SPINLOCK_INIT;
// 加锁
OSSpinLockLock(&_lock);
加锁 和 解锁 中间放 多个线程访问的资源
// 解锁
OSSpinLockUnlock(&_lock);
二、os_unfair_lock
互斥锁:等待的线程会处于休眠状态。
- 从底层调用看(下面会从汇编角度来分析自旋锁和互斥锁),等待os-unfair-lock锁的线程会处于休眠状态,而并非忙等,所以说虽然os-unfair-lock用于取代OSSpinLock,但是os-unfair-lock并不是自旋锁。
#import "ViewController.h"
#import <os/lock.h>
@interface ViewController (){
os_unfair_lock lock;
}
@property (assign, nonatomic) int ticketsCount;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
lock = OS_UNFAIR_LOCK_INIT;
[self ticketTest];
}
- (void)saleTicket
{
os_unfair_lock_lock(&lock);
int oldTicketsCount = self.ticketsCount;
sleep(.2);
oldTicketsCount--;
self.ticketsCount = oldTicketsCount;
NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
// 解锁
os_unfair_lock_unlock(&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];
}
});
}
@end
三、pthread_mutex
-
互斥锁
屏幕快照 2019-05-08 下午1.58.30.png
#import "ViewController.h"
#import <pthread.h>
@interface ViewController ()
@property (assign, nonatomic) int ticketsCount;
@property(nonatomic,assign)pthread_mutex_t mutex;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 初始化锁的属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
//初始化锁
pthread_mutex_init(&_mutex, &attr);
//销毁锁的属性
pthread_mutexattr_destroy(&attr);
[self ticketTest];
}
- (void)saleTicket
{
//加锁
pthread_mutex_lock(&_mutex);
int oldTicketsCount = self.ticketsCount;
sleep(.2);
oldTicketsCount--;
self.ticketsCount = oldTicketsCount;
NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
//解锁
pthread_mutex_unlock(&_mutex);
}
//销毁锁
- (void)dealloc{
pthread_mutex_destroy(&_mutex);
}
- (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];
}
});
}
@end
3.1、递归锁
#import "ViewController.h"
#import <pthread.h>
@interface ViewController ()
@property(nonatomic,assign)pthread_mutex_t mutex;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
//初始化锁
pthread_mutex_init(&_mutex, NULL);
[self test1];
}
- (void)test1{
//加锁
pthread_mutex_lock(&_mutex);
NSLog(@"测试一");
static int i = 0;
I++;
if (i<10) {
[self test1];
}
//解锁
pthread_mutex_unlock(&_mutex);
}
- 初始化属性传Null,可以正常使用
- 以上递归调用会产生死锁现象
解决方案:设置为递归锁
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
#import "ViewController.h"
#import <pthread.h>
@interface ViewController ()
@property (assign, nonatomic) int ticketsCount;
@property(nonatomic,assign)pthread_mutex_t mutex;
@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(&_mutex, &attr);
//销毁锁的属性
pthread_mutexattr_destroy(&attr);
[self test1];
}
- (void)test1{
//加锁
pthread_mutex_lock(&_mutex);
NSLog(@"测试一");
static int i = 0;
I++;
if (i<10) {
[self test1];
}
//解锁
pthread_mutex_unlock(&_mutex);
}
- 递归锁允许同一线程重复加锁、解锁。但不允许不同线程重复加锁,这样既保证了线程安全又解决了线程中递归调用。
3.1、条件
屏幕快照 2019-05-08 下午4.12.04.png需求描述:开启两个线程,线程一给可变的array删除最后一个元素,线程二给可变的array添加元素。如果array.count = 0则不需要删除元素。
#import "ViewController.h"
#import <pthread.h>
@interface ViewController ()
@property(nonatomic,strong)NSMutableArray *array;
@property(nonatomic,assign)pthread_mutex_t mutex;
@property(nonatomic,assign)pthread_cond_t condtion;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.array = [NSMutableArray array];
//初始化条件
pthread_cond_init(&_condtion, NULL);
//初始化锁
pthread_mutex_init(&_mutex, NULL);
[[[NSThread alloc]initWithTarget:self selector:@selector(removeData) object:nil]start];
sleep(2);
[[[NSThread alloc]initWithTarget:self selector:@selector(addData) object:nil]start];
}
- (void)removeData{
//加锁
pthread_mutex_lock(&_mutex);
NSLog(@"remove");
if (self.array.count == 0) {
pthread_cond_wait(&_condtion, &_mutex);
}
[self.array removeLastObject];
NSLog(@"删除元素");
pthread_mutex_unlock(&_mutex);
}
- (void)addData{
//加锁
pthread_mutex_lock(&_mutex);
[self.array addObject:@"0"];
NSLog(@"添加元素");
pthread_cond_signal(&_condtion);
//解锁
pthread_mutex_unlock(&_mutex);
}
屏幕快照 2019-05-08 下午4.23.00.png
当 线程一执行了第43行: pthread_cond_wait(&_condtion, &_mutex);这句代码,线程一会放开锁进入休眠等待状态,线程二就可以获得锁进行加锁。当线程二执行了 pthread_cond_signal(&_condtion);这句代码后,线程一会被唤醒再次加上锁,开始从第45行代码之后继续执行。因此不管怎么样打印的结果都是先添加元素,再删除元素。总结来说pthread_mutex的应用场景应该是线程依赖。
四、OC版的锁
- NSLock:是对mutex普通锁的封装
- NSRecursiveLock:是对mutex递归锁的封装
- NSCondition:是对mutex和cond的封装
- NSConditionLock是对NSCondition的进一步封装
4.1、 NSLock
屏幕快照 2019-05-09 上午9.48.45.png#import <Foundation/NSObject.h>
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,assign)int ticketsCount;
@property(nonatomic,strong)NSLock *lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.lock = [[NSLock alloc]init];
[self ticketTest];
}
- (void)saleTicket
{
//加锁
[self.lock lock];
int oldTicketsCount = self.ticketsCount;
sleep(.2);
oldTicketsCount--;
self.ticketsCount = oldTicketsCount;
NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
//解锁
[self.lock unlock];
}
- (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];
}
});
}
4.2、NSRecursiveLock
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)NSRecursiveLock *recursiveLock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.recursiveLock = [[NSRecursiveLock alloc]init];
[self test1];
}
- (void)test1{
//加锁
[self.recursiveLock lock];
NSLog(@"测试一");
static int i = 0;
I++;
if (i<10) {
[self test1];
}
//解锁
[self.recursiveLock unlock];
}
4.3、NSCondition
屏幕快照 2019-05-09 上午9.49.07.png#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)NSMutableArray *array;
@property(nonatomic,assign)int ticketsCount;
@property(nonatomic,strong)NSCondition *condition;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.array = [NSMutableArray array];
self.condition = [[NSCondition alloc]init];
[[[NSThread alloc]initWithTarget:self selector:@selector(removeData) object:nil]start];
sleep(2);
[[[NSThread alloc]initWithTarget:self selector:@selector(addData) object:nil]start];
}
- (void)removeData{
[self.condition lock];
NSLog(@"remove");
if (self.array.count == 0) {
[self.lockcondition wait];
}
[self.array removeLastObject];
NSLog(@"删除元素");
[self.condition unlock];
}
- (void)addData{
[self.condition lock];
[self.array addObject:@"0"];
NSLog(@"添加元素");
[self.condition signal];
[self.condition unlock];
}
4.4、NSConditionLock
屏幕快照 2019-05-09 上午9.51.21.png#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)NSConditionLock *conditionlock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.conditionlock = [[NSConditionLock alloc]initWithCondition:1];
[self test];
}
- (void)test{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
[self.conditionlock lockWhenCondition:2];
NSLog(@"线程2");
[self.conditionlock unlock];
});
sleep(10);
dispatch_async(queue, ^{
[self.conditionlock lockWhenCondition:1];
NSLog(@"线程1");
[self.conditionlock unlockWithCondition:2];
});
}
屏幕快照 2019-05-09 下午3.52.53.png