引导:多线程安全隐患?
一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源,比如多个线程访问同一个对象、同一个变量、同一个文件,当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题。
1.多线程安全隐患分析
多个线程访问同一个变量2.多线程安全隐患解决方案
使用线程同步技术(同步,就是协同步调,按预定的先后次序进行),常见的线程同步技术是:加锁
3.iOS的线程同步方案
OSSpinLock
os_unfair_lock
pthread_mutex
dispatch_semaphore
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSRecursiveLock
NSCondition
NSConditionLock
@synchronized
下面将对上述所有同步方案一一介绍。
1.OSSpinLock
- 介绍:
1.OSSpinLock叫做”自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源;
2.目前已经不再安全,可能会出现优先级反转问题;
3.如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁;
4.需要导入头文件#import <libkern/OSAtomic.h> - 代码
#import "ViewController.h"
#import <libkern/OSAtomic.h>
@interface ViewController ()
@property (nonatomic, assign) NSInteger allTicketCount;
@property (nonatomic, assign) OSSpinLock lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.allTicketCount = 10;
self.lock = OS_SPINLOCK_INIT;
// 开启两条线程 各卖五张票
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self sellTicket];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self sellTicket];
}
});
}
// 卖票
- (void)sellTicket {
// 加锁
OSSpinLockLock(&_lock);
sleep(1);
self.allTicketCount --;
NSLog(@"还有%ld张票 -- %@",self.allTicketCount,[NSThread currentThread]);
// 解锁
OSSpinLockUnlock(&_lock);
}
@end
- 运行结果
2.os_unfair_lock
- 介绍:
1.os_unfair_lock用于取代不安全的OSSpinLock ,从iOS10开始才支持;
2.从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等;
3.需要导入头文件#import <os/lock.h> - 代码
#import "ViewController.h"
#import <os/lock.h>
@interface ViewController ()
@property (nonatomic, assign) NSInteger allTicketCount;
@property (nonatomic, assign) os_unfair_lock lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.allTicketCount = 10;
self.lock = OS_UNFAIR_LOCK_INIT;;
// 开启两条线程 各卖五张票
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self sellTicket];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self sellTicket];
}
});
}
// 卖票
- (void)sellTicket {
// 加锁
os_unfair_lock_lock(&_lock);
sleep(1);
self.allTicketCount --;
NSLog(@"还有%ld张票 -- %@",self.allTicketCount,[NSThread currentThread]);
// 解锁
os_unfair_lock_unlock(&_lock);
}
@end
-
运行结果
结果
3.pthread_mutex
- 介绍:
1.mutex叫做”互斥锁”,等待锁的线程会处于休眠状态
2.需要导入头文件#import <pthread.h> - 代码
#import "ViewController.h"
#import <pthread.h>
@interface ViewController ()
@property (nonatomic, assign) NSInteger allTicketCount;
@property (assign, nonatomic) pthread_mutex_t mutex;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.allTicketCount = 10;
pthread_mutex_init(&_mutex, NULL);
// 开启两条线程 各卖五张票
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self sellTicket];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self sellTicket];
}
});
}
// 卖票
- (void)sellTicket {
// 加锁
pthread_mutex_lock(&_mutex);
sleep(1);
self.allTicketCount --;
NSLog(@"还有%ld张票 -- %@",self.allTicketCount,[NSThread currentThread]);
// 解锁
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc
{
pthread_mutex_destroy(&_mutex);
}
@end
-
运行结果
结果
4.pthread_mutex 递归锁
- 介绍
递归锁:允许同一个线程对一把锁进行重复加锁 - 代码
#import "ViewController.h"
#import <pthread.h>
@interface ViewController ()
@property (nonatomic, assign) NSInteger allTicketCount;
@property (assign, nonatomic) pthread_mutex_t mutex;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.allTicketCount = 10;
// 初始化属性
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 otherTest];
}
- (void)otherTest
{
pthread_mutex_lock(&_mutex);
NSLog(@"%s -- %@", __func__,[NSThread currentThread]);
static int count = 0;
if (count < 10) {
count++;
// 一般情况下上锁之后,执行到此处会卡住变成死锁,因为接下来的代码试图上锁,但第一次上的锁还没有解锁 所以产生死锁,使用递归锁之后,代码能递归执行。
[self otherTest];
}
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc
{
pthread_mutex_destroy(&_mutex);
}
@end
-
运行结果
结果
5.pthread_mutex 条件锁
- 介绍
操作背景:开启两个线程,一个往数组中添加元素,一个从数组中删除元素,但删除元素的操作应该在数组中有元素的前提下进行。 - 代码
#import "ViewController.h"
#import <pthread.h>
@interface ViewController ()
@property (assign, nonatomic) pthread_mutex_t mutex;
@property (assign, nonatomic) pthread_cond_t cond;
@property (strong, nonatomic) NSMutableArray *data;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.data = [NSMutableArray array];
// 初始化锁
pthread_mutex_init(&_mutex, NULL);
// 初始化条件
pthread_cond_init(&_cond, NULL);
// 两个线程同时操作数据
[self otherTest];
}
- (void)otherTest
{
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self add];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self remove];
}
});
}
- (void)add {
pthread_mutex_lock(&_mutex);
sleep(1);
[self.data addObject:@"test"];
NSLog(@"add - %@",[NSThread currentThread]);
// 对等待这个条件的线程发送信号 可以执行了
pthread_cond_signal(&_cond);
// // 对等待所有条件的线程发送信号 可以执行了
// pthread_cond_broadcast(&_cond);
pthread_mutex_unlock(&_mutex);
}
- (void)remove {
pthread_mutex_lock(&_mutex);
// 如果data.count为0,就进入等待状态
if (self.data.count == 0) {
pthread_cond_wait(&_cond, &_mutex);
}
sleep(1);
[self.data removeLastObject];
NSLog(@"remove- %@",[NSThread currentThread]);
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
@end
-
运行结果
结果
6.NSLock
- 介绍
pthread_mutex普通锁的封装 - 代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, assign) NSInteger allTicketCount;
@property (nonatomic, strong) NSLock *lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.lock = [[NSLock alloc] init];
self.allTicketCount = 10;
// 两个线程同时操作数据
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self sellTicket];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self sellTicket];
}
});
}
// 卖票
- (void)sellTicket {
// 加锁
[self.lock lock];
sleep(1);
self.allTicketCount --;
NSLog(@"还有%ld张票 -- %@",self.allTicketCount,[NSThread currentThread]);
// 解锁
[self.lock unlock];
}
@end
-
运行结果
结果
7.NSRecursiveLock
- 介绍
对pthread_mutex 递归锁的封装 - 代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSRecursiveLock *lock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.lock = [[NSRecursiveLock alloc] init];
[self otherTest];
}
- (void)otherTest {
// 加锁
[self.lock lock];
static NSInteger count = 0;
if (count < 10) {
count ++;
NSLog(@" -- %ld",count);
[self otherTest];
}
// 解锁
[self.lock unlock];
}
@end
-
运行结果
结果
8.NSCondition
- 介绍
pthread_mutex和pthread_cond的封装 - 代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSCondition *cond;
@property (nonatomic, strong) NSMutableArray *data;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.cond = [[NSCondition alloc] init];
self.data = [NSMutableArray array];
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self add];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self remove];
}
});
}
- (void)add {
[self.cond lock];
[self.data addObject:@"test"];
NSLog(@"add - %@",[NSThread currentThread]);
[self.cond unlock];
}
- (void)remove {
[self.cond lock];
if (self.data.count == 0) {
[self.cond wait];
}
[self.data removeLastObject];
NSLog(@"remove - %@",[NSThread currentThread]);
[self.cond unlock];
}
@end
-
运行结果
结果
9.NSConditionLock
- 介绍
NSCondition的进一步封装,可以设置具体的条件值 - 代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) NSConditionLock *condLock;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.condLock = [[NSConditionLock alloc] initWithCondition:1];
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
[self one];
});
dispatch_async(queue, ^{
[self two];
});
dispatch_async(queue, ^{
[self three];
});
}
- (void)one {
[self.condLock lock];
NSLog(@"one");
[self.condLock unlockWithCondition:2];
}
- (void)two {
[self.condLock lockWhenCondition:2];
NSLog(@"two");
[self.condLock unlockWithCondition:3];
}
- (void)three {
[self.condLock lockWhenCondition:3];
NSLog(@"three");
[self.condLock unlock];
}
@end
-
运行结果
结果
10.dispatch_semaphore_t 信号量
- 介绍
1.信号量的初始值,可以用来控制线程并发访问的最大数量;
2.信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步; - 代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) dispatch_semaphore_t sema;
@property (nonatomic, assign) NSInteger ticketCount;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.sema = dispatch_semaphore_create(1);
self.ticketCount = 10;
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self sellTicket];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self sellTicket];
}
});
}
- (void)sellTicket {
dispatch_semaphore_wait(self.sema, DISPATCH_TIME_FOREVER);
sleep(1);
self.ticketCount --;
NSLog(@" 还有%ld张票 -- %@",self.ticketCount,[NSThread currentThread]);
dispatch_semaphore_signal(self.sema);
}
@end
-
运行结果
结果
11.@synchronized
- 介绍
1.@synchronized是对mutex递归锁的封装;
2.@synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作; - 代码
#import "ViewController.h"
@interface ViewController ()
@property (nonatomic, strong) dispatch_semaphore_t sema;
@property (nonatomic, assign) NSInteger ticketCount;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.sema = dispatch_semaphore_create(1);
self.ticketCount = 10;
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self sellTicket];
}
});
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 5; i ++) {
[self sellTicket];
}
});
}
- (void)sellTicket {
@synchronized (self) {
sleep(1);
self.ticketCount --;
NSLog(@" 还有%ld张票 -- %@",self.ticketCount,[NSThread currentThread]);
}
}
@end
-
运行结果
结果
4.同步方案性能对比
- 性能从高到低
os_unfair_lock
OSSpinLock
dispatch_semaphore
pthread_mutex
dispatch_queue(DISPATCH_QUEUE_SERIAL)
NSLock
NSCondition
pthread_mutex(recursive)
NSRecursiveLock
NSConditionLock
@synchronized
参考:MJ老师的视频课程。
网友评论