线程同步解决方案,各种锁

看了YY系列作者的不再安全的 OSSpinLock,有心整理一下iOS上会用到的锁,包括OSSpinLockpthread_mutexNSConditionNSRecursiveLockNSConditionLock,以及能实现锁功能的dispatch_semaphore@synchronized, OSSpinLock和pthread_mutex的可见性为内核态,其他为用户态。

谈及各种同步机制的PK,个人觉得这篇文章整理得比较不错,推荐给大家。(备注:不过纠正一点,这篇文章说MacOS/iOS没有提供读写锁,实际上pthread库里是有提供读写锁的)

以下会简单整理每种同步方案的特性和使用方法,使用方法都以Objctive-C中不安全NSMutableDictionary的读写为例,先创建NSMutableDictionary:

1
NSMutableDictionary *dict = [NSMutableDictionary dictionary];

OSSpinLock

特性:

OS X/iOS自有的自旋锁,线程等待取锁不挂起,直接保持空转,做忙等待,适用锁区比较短的场景,不支持递归。
但OSSpinLock现在已经不再安全,在不同优先级的多线程调度中,会有优先级反转问题,破坏spin lock。

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建
OSSpinLock _lock = OS_SPINLOCK_INIT;
// 加锁解锁:
OSSpinLockLock(&_lock);
[dict setObject:@"boz" forKey:@"trial"];
OSSpinLockUnlock(&_lock);
OSSpinLockLock(&_lock);
id obj = [dict objectForKey:@"trial"];
OSSpinLockUnlock(&_lock);
//锁尝试:
bool success = OSSpinLockTry(&_lock);

pthread_mutex

特性:

多线程库(pthread)基于互斥体的同步锁,是最常用的锁对象,并且支持递归,性能和稳定性都是公认最好的,比较推荐使用。pthread_mutex也有不好的地方,一次只能一个线程持有锁,且阻塞等待对象(阻塞等待对象也是他比自旋锁慢的原因)。

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 创建 pthread_mutex_t
pthread_mutex_t _mutex;
// 创建 mutexattr
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
// 指定 pthread_mutex_t type
// 指定 type 为PTHREAD_MUTEX_RECURSIVE 即可支持递归
1. pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
// 创建
pthread_mutex_init(&_mutex, &attr);
pthread_mutexattr_destroy(&attr);
// 写数据
pthread_mutex_lock(&_mutex);
[dict setObject:@"boz" forKey:@"trial"];
pthread_mutex_unlock(&_mutex);
// 读数据
pthread_mutex_lock(&_mutex);
id obj = [dict objectForKey:@"trail"];
pthread_mutex_unlock(&_mutex);
// 释放 pthread_mutex_t
pthread_mutex_destroy(&_mutex);

dispatch_semaphore

特性:

GCD中用于控制多线程并发的信号量,可以通过wait/signal的信号事件控制多线程最大并发数,当最大并发数控制在1的时候,就可以当做同步锁使用,但这种方式不支持递归。

使用:

1
2
3
4
5
6
// 创建
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
// 加解锁
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[dict setObject:@"boz" forKey:@"trial"];
dispatch_semaphore_signal(semaphore);

NSLock/NSRecursiveLock

特性:

都是Apple官方实现提供的面向对象的同步锁,都遵循Objective-C的NSLocking协议,NSLock支持tryLock,NSRecursiveLock支持递归(递归锁).

使用:

1
2
3
4
5
6
7
8
// 使用方式一样 都已Lock标识
id lock = [[Lock alloc] init];
// NSLock 支持
bool success = [lock tryLock];
// 加解锁
[lock lock];
[dict setObject:@"boz" forKey:@"trial"];
[lock unLock];

NSCondition/NSConditionLock

特性:

基于信号量方式实现的锁对象,用来解决类似生产者消费者问题,前者提供单独的信号量管理接口,相比后者用法上可以更为灵活,而后者在接口上更为直接、实用;

使用:

NSCondition/NSConditionLock是用来解决类似生产者消费者问题,并不适用保护不安全NSMutableDictionary的读写,下面简单贴出NSCondition实现的生产者消费者问题代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
- (void)testStart
{
products = [[NSMutableArray alloc] init];
condition = [[NSCondition alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self createProducter];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self createConsumenr];
});
}
- (void)createConsumenr
{
[condition lock];
while ([products count] == 0) {
// 消费者等待生产者生成
[condition wait];
}
// 消费者消费
[products removeObjectAtIndex:0];
[condition unlock];
}
- (void)createProducter
{
[condition lock];
[products addObject:[[NSObject alloc] init]];
// 生产者生产
[condition signal];
[condition unlock];
}

@synchronized(){}

特性:

Objective-C实现的同步语法,可以给任何Objective-C对象加锁,block里执行锁区代码。@synchronizd(){}在多线程同步方案中效率是最低,原因是他的block会隐式添加异常处理来保护代码块,但@synchronizd(){}在代码书写方面比较简便。

使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 写数据
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized (dict) {
[dict setObject:@"boz" forKey:@"trail"];
}
});
// 读数据
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized (dict) {
id obj = [dict objectForKey:@"trail"];
}
});

property 之 atomic

Objective-C中属性的可选操作atomic,利用get/set接口的属性实现原子操作,以确保对象的属性变量在多线程中读写安全,这也实现了同步读写操作。

引用:

  1. 起底多线程同步锁:http://springox.w18.net/?p=685
  2. 不再安全的 OSSpinLock:http://blog.ibireme.com/2016/01/16/spinlock_is_unsafe_in_ios
  3. 对iOS锁的一些研究:http://blog.csdn.net/meegomeego/article/details/39546765