一、iOS中常见的多线程方案
二、GCD
- GCD中有2个用来执行任务的函数
- 用同步的方式执行任务
void dispatch_sync(dispatch_queue_t queue, DISPATCH_NOESCAPE dispatch_block_t block);
- queue:队列
- block:任务
- 用异步的方式执行任务
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);
- GCD源码:https://github.com/apple/swift-corelibs-libdispatch
- 用同步的方式执行任务
- GCD的队列
- 并发队列(Concurrent Dispatch Queue)
- 可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
- 并发功能只能在异步(dispatch_async)函数下才有效
- 串行队列(Serial Dispatch Queue)
- 让任务一个接着一个的执行(一个任务执行完毕后,再执行下一个任务)
- 并发队列(Concurrent Dispatch Queue)
容易混淆的术语
- 有4个术语比较容易混淆:同步、异步、串行、并发
- 同步和异步主要影响:能不能开启新的线程
- 同步:在当前线程中执行任务,不具备开启新线程的能力
- 异步:在新的线程中执行任务,具备开启新线程的能力
- 并发和串行主要影响:任务的执行方式
- 并发:多个任务并发(同时)执行
- 串行:一个任务执行完毕后,再执行下一个任务
- 同步和异步主要影响:能不能开启新的线程
- 有4个术语比较容易混淆:同步、异步、串行、并发
各种队列的执行效果
判断是否产生死锁:使用sync函数往当前串行队列中添加任务,会产生死锁
队列组的使用
- 异步并发执行任务1、任务2
- 等任务1、任务2都执行完毕后,再回到主线程执行任务3
dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任务1 - %@",[NSThread currentThread]);
}
});
dispatch_group_async(group, queue, ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任务2 - %@",[NSThread currentThread]);
}
});
dispatch_group_notify(group, queue, ^{
dispatch_async(dispatch_get_main_queue(), ^{
for (int i = 0; i < 3; i++) {
NSLog(@"任务3 - %@",[NSThread currentThread]);
}
});
});
三、线程安全
- 多线程安全隐患
- 资源共享
- 一块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
- 比如多个线程访问同一个对象、同一个变量、同一个文件
- 多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题(卖票问题、存取钱问题)
- 资源共享
- 解决方案
- 使用线程同步技术(同步,就是协同步调,按预定的先后次序进行)
- 常见的线程同步技术:加锁
- 加锁前
- 加锁后
四、线程同步方案
OSSpinLock
- OSSpinLock叫做“自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源
目前已经不再安全,可能会出现优先级反转问题
- 如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁
- 需要导入头文件
#import <libkern/OSAtomic.h>
@property (nonatomic, assign) OSSpinLock moneyLock; self.moneyLock = OS_SPINLOCK_INIT; // 初始化
bool result = OSSpinLockTry(&_moneyLock); // 尝试加锁(如果需要等待就不加锁,直接返回false;如果不需要等待就加锁,返回true)
OSSpinLockLock(&_moneyLock); // 加锁
OSSpinLockUnlock(&_moneyLock); // 解锁
os_unfair_lock
- os_unfair_lock用于取代不安全的OSSpinLock,从iOS10才开始支持
- 从底层调用来看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
- 需要导入头文件
#import <os/lock.h>
@property (nonatomic, assign) os_unfair_lock moneyLock; self.moneyLock = OS_UNFAIR_LOCK_INIT;
os_unfair_lock_lock(&_moneyLock);
os_unfair_lock_unlock(&_moneyLock);pthread_mutex
- mutex叫做“互斥锁”,等待锁的线程会处于休眠状态
- 需要导入头文件
#import <pthread.h>
/* * Mutex type attributes
*/
#define PTHREAD_MUTEX_NORMAL 0
#define PTHREAD_MUTEX_ERRORCHECK 1
#define PTHREAD_MUTEX_RECURSIVE 2 // 递归锁
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL
@property (nonatomic, assign) pthread_mutex_t moneyMutex;
- (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);
}
// 加锁
pthread_mutex_lock(&_moneyMutex);
// 解锁
pthread_mutex_unlock(&_moneyMutex);
// 销毁锁
pthread_mutex_destroy(&_moneyMutex);
pthread_mutex_destroy(&_ticketMutex);pthread_mutex - 条件
#import <pthread.h> @interface MutexDemo3 ()
@property (nonatomic, assign) pthread_mutex_t mutex;
@property (nonatomic, assign) pthread_cond_t cond;
@property (nonatomic, strong) NSMutableArray *data;
@end
@implementation MutexDemo3
- (instancetype)init
{
self = [super init];
if (self) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
pthread_mutex_init(&_mutex, &attr);
pthread_mutexattr_destroy(&attr);
self.data = [NSMutableArray array];
pthread_cond_init(&_cond, NULL);
}
return self;
}
- (void)otherTest {
[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}
- (void)__remove {
pthread_mutex_lock(&_mutex);
if ([self.data count] == 0) {
pthread_cond_wait(&_cond, &_mutex);
NSLog(@"移除数据 - %@",[NSThread currentThread]);
}
[self.data removeLastObject];
pthread_mutex_unlock(&_mutex);
}
- (void)__add {
pthread_mutex_lock(&_mutex);
[self.data addObject:@"test"];
NSLog(@"添加了数据 - %@",[NSThread currentThread]);
pthread_cond_signal(&_cond);
// pthread_cond_broadcast(&_cond); // 通知所有等待的锁
pthread_mutex_unlock(&_mutex);
}
- (void)dealloc
{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
@endNSLock、NSRecursiveLock
- NSLock是对mutex普通锁的封装
@property (nonatomic, strong) NSLock *moneyLock; self.moneyLock = [[NSLock alloc] init];
[self.moneyLock lock];
[self.moneyLock unlock];- NSRecursiveLock是对mutex递归锁的封装
NSCondition是对mutex和cond的封装
#import "NSConditionDemo.h" @interface NSConditionDemo ()
@property (nonatomic, strong) NSCondition *condition;
@property (nonatomic, strong) NSMutableArray *data;
@end
@implementation NSConditionDemo
- (instancetype)init
{
self = [super init];
if (self) {
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];
}
- (void)__remove {
[self.condition lock];
if (self.data.count == 0) {
[self.condition wait];
}
[self.data removeLastObject];
NSLog(@"删除了数据 - %@",[NSThread currentThread]);
[self.condition unlock];
}
- (void)__add {
[self.condition lock];
[self.data addObject:@"test"];
NSLog(@"添加了数据 - %@",[NSThread currentThread]);
[self.condition signal];
// [self.condition broadcast];
[self.condition unlock];
}
- (void)dealloc
{
NSLog(@"%s",__func__);
}
@endNSConditionLock
- NSConditionLock是对NSCondition的进一步封装,可以设置具体的条件值
#import "NSConditionLockDemo.h" @interface NSConditionLockDemo ()
@property (nonatomic, strong) NSConditionLock *condLock;
@end
@implementation NSConditionLockDemo
- (instancetype)init
{
self = [super init];
if (self) {
// self.condLock = [[NSConditionLock alloc] initWithCondition:0];
self.condLock = [[NSConditionLock alloc] init]; // 默认condition是0
}
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.condLock lockWhenCondition:0];
[self.condLock lock]; // 不管condition是多少,都能加锁
NSLog(@"one - %@",[NSThread currentThread]);
[self.condLock unlockWithCondition:1];
}
- (void)__two {
[self.condLock lockWhenCondition:1];
NSLog(@"two - %@",[NSThread currentThread]);
[self.condLock unlockWithCondition:2];
}
- (void)__three {
[self.condLock lockWhenCondition:2];
NSLog(@"three - %@",[NSThread currentThread]);
[self.condLock unlock];
}
- (void)dealloc
{
NSLog(@"%s",__func__);
}
@enddispatch_semaphore
- semaphore叫做“信号量”
- 信号量的初始值,可以用来控制线程并发访问的最大数量
- 信号量的初始值为1,代表同时只允许1条线程访问资源,保证线程同步
#import "SemaphoreDemo.h" @interface SemaphoreDemo ()
@property (nonatomic, strong) dispatch_semaphore_t semaphore;
@property (nonatomic, strong) dispatch_semaphore_t moneySemaphore;
@property (nonatomic, strong) dispatch_semaphore_t ticketSemaphore;
@end
@implementation SemaphoreDemo
- (instancetype)init
{
self = [super init];
if (self) {
self.semaphore = dispatch_semaphore_create(5);
self.moneySemaphore = dispatch_semaphore_create(1);
self.ticketSemaphore = dispatch_semaphore_create(1);
}
return self;
}
- (void)otherTest {
for (int i = 0; i < 15; i++) {
[[[NSThread alloc] initWithTarget:self selector:@selector(__test) object:nil] start];
}
}
- (void)__test {
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"%@",[NSThread currentThread]);
sleep(2);
dispatch_semaphore_signal(self.semaphore);
}
- (void)__saveMoney {
dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
[super __saveMoney];
dispatch_semaphore_signal(self.moneySemaphore);
}
- (void)__drawMoney {
dispatch_semaphore_wait(self.moneySemaphore, DISPATCH_TIME_FOREVER);
[super __drawMoney];
dispatch_semaphore_signal(self.moneySemaphore);
}
- (void)__saleTicket {
dispatch_semaphore_wait(self.ticketSemaphore, DISPATCH_TIME_FOREVER);
[super __saleTicket];
dispatch_semaphore_signal(self.ticketSemaphore);
}
- (void)dealloc
{
NSLog(@"%s",__func__);
}
@endsynchronized
- @synchronized是对mutex递归锁的封装
- 源码查看:objc4的objc-sync.mm文件
- @synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁、解锁操作
- (void)__saleTicket { static NSObject *lock;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
lock = [[NSObject alloc] init];
});
@synchronized (lock) {
[super __saleTicket];
}
}
- (void)otherTest {
NSLog(@"%s",__func__);
@synchronized (self) {
[self otherTest];
}
}线程同步方案的性能比较
- 性能从高到低排序
- os_unfair_lock
- OSSpinLock
- dispatch_semaphore
- pthread_mutex
- dispatch_queue(DISPATCH_QUEUE_SERIAL)
- NSLock
- NSCondition
- pthread_mutex(recursive)
- NSRecursiveLock
- NSConditionLock
- @synchronized
- 性能从高到低排序
自旋锁、互斥锁比较
- 什么情况使用自旋锁?
- 预计线程等待锁的时间很短
- 加锁的代码(临界区)经常被调用,但竞争情况很少发生
- CPU资源不紧张
- 多核处理器
- 什么情况使用互斥锁?
- 预计线程等待锁的时间比较长
- 单核处理器
- 临界区有IO操作
- 临界区代码复杂或者循环量大
- 临界区竞争非常激烈
#### 五、atomic
- 什么情况使用自旋锁?
atomic用于保证属性setter、getter的原子性操作,相当于在getter和setter内部加了线程同步的锁
可以参考源码objc4的objc-accessors.mm
并不能保证使用属性的过程是线程安全的
六、iOS中的读写安全方案
多读单写
- 场景
- 同一时间,只能有1个线程进行写的操作
- 同一时间,允许有多个线程进行读的操作
- 同一时间,不允许既有写的操作,又有读的操作
- 多读单写,经常用于文件数据的读写操作,实现方案有:
- pthread_rwlock:读写锁
- dispatch_barrier_async:异步栅栏调用
- 场景
pthread_rwlock
#import "RWLockDemo.h" #import <pthread.h>
@interface RWLockDemo ()
@property (nonatomic, assign) pthread_rwlock_t rwLock;
@end
@implementation RWLockDemo
- (instancetype)init
{
self = [super init];
if (self) {
pthread_rwlock_init(&_rwLock, NULL);
}
return self;
}
- (void)otherTest {
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; i++) {
dispatch_async(queue, ^{
[self read];
});
dispatch_async(queue, ^{
[self write];
});
}
}
- (void)read {
pthread_rwlock_rdlock(&_rwLock);
sleep(1);
NSLog(@"%s",__func__);
pthread_rwlock_unlock(&_rwLock);
}
- (void)write {
pthread_rwlock_wrlock(&_rwLock);
sleep(1);
NSLog(@"%s",__func__);
pthread_rwlock_unlock(&_rwLock);
}
- (void)dealloc
{
pthread_rwlock_destroy(&_rwLock);
}
@enddispatch_barrier_async
- 这个函数传入的并发队列必须是自己通过
dispatch_queue_create
创建的 - 如果传入的是一个串行队列或者全局的并发队列,那这个函数便等同于
dispatch_async
函数的效果
#import "BarrierDemo.h" @interface BarrierDemo ()
@property (nonatomic, strong) dispatch_queue_t rwLock;
@end
@implementation BarrierDemo
- (instancetype)init
{
self = [super init];
if (self) {
self.rwLock = dispatch_queue_create("rw-queue", DISPATCH_QUEUE_CONCURRENT);
}
return self;
}
- (void)otherTest {
for (int i = 0; i < 10; i++) {
[self read];
[self read];
[self read];
[self write];
}
}
- (void)read {
dispatch_async(self.rwLock, ^{
sleep(1);
NSLog(@"read");
});
}
- (void)write {
dispatch_barrier_async(self.rwLock, ^{
sleep(1);
NSLog(@"write");
});
}
@end- 这个函数传入的并发队列必须是自己通过
--Posted from Rpc