多线程情况下访问共享资源需要进行线程同步,线程同步一般都用锁实现。从操作系统层面,锁的实现有临界区、事件、互斥量、信号量等。这里讲一下iOS中多线程同步的方式
以下四种锁NSLock、NSRecursiveLock、NSConditionLock、NSCondition都实现了协议NSLocking
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
1、NSLock
最简单的锁,调用lock获取锁,unlock释放锁。如果其它线程已经调用lock获取了锁,当前线程调用lock方法会阻塞当前线程,直到其它线程调用unlock释放锁为止。NSLock使用简单,在项目中用的最多。用法如下:
- (void)threadAMethod
{
[dataLock lock];
//访问共享数据
[dataLock unlock];
}
- (void)threadBMethod
{
if([dataLock tryLock]) //可以直接获取锁
{
//访问共享数据
[dataLock unlock];
}
else
{
[dataLock lock];
//访问共享数据
[dataLock unlock];
}
}
NSLock的- (BOOL)tryLock方法尝试立即去获取锁,成功返回YES,否则NO;方法- (BOOL)lockBeforeDate:(NSDate *)limit尝试在指定时间之前去获取锁,成功返回YES,否则NO。
2、NSRecursiveLock
递归锁主要用来解决同一个线程频繁获取同一个锁而不造成思索的问题。注意lock和unlock调用必须配对
- (void)threadAMethod1
{
[dataLock lock];
//访问共享数据
//调用threadAMethod2
[self threadAMethod2];
[dataLock unlock];
}
- (void)threadAMethod2
{
if([dataLock tryLock]) //可以直接获取锁
{
//访问共享数据
[dataLock unlock];
}
else
{
[dataLock lock];
//访问共享数据
[dataLock unlock];
}
}
3、NSConditionLock
条件锁,可以设置自定义条件来获取锁。比如生产者消费者模型可以用条件锁来实现
- (void)producer
{
while (1)
{
[theLock lockWhenCondition:NO_DATA];
// create data
[theLock unlockWithCondition:HAS_DATA];
}
}
- (void)consumer
{
while (1)
{
if ([theLock tryLockWhenCondition:HAS_DATA])
{
// display data
[theLock unlockWithCondition:NO_DATA];
}
}
}
4、NSCondition
条件,操作系统中信号量的实现,方法- (void)wait和- (BOOL)waitUntilDate:(NSDate *)limit用来等待锁直至锁有信号
方法- (void)signal和- (void)broadcast使condition有信号,通知等待condition的线程,变成非阻塞状态.用法如下:
- (void)threadAMethod
{
[dataLock lock];
//创建共享数据
[dataLock signal]; //告诉线程B,我已经创建完数据了,
// [dataLock broadcast];
[dataLock unlock];
}
- (void)threadBMethod
{
[dataLock lock];
[dataLock wait];
//访问共享数据
[dataLock unlock];
}
5、@property(atomic)
属性加上atomic关键字编译器会自动给该属性生成代码用以多线程访问同步,一般我们在定义属性的时候用nonatomic,避免性能损失
6、@synchronized (obj)
@synchronized指令其实是一个对象锁,用起来简单.
@synchronized指令使用obj为该锁的唯一标识,只有当标识相同时,才为满足互斥,如果线程1和线程2中的@synchronized后面的obj不相同,则不会互斥。@synchronized优点是我们不需要在代码中显式的创建锁对象,使用简单; 缺点是@synchronized会隐式的添加一个异常处理例程该处理例程会在异常抛出的时候自动的释放互斥锁,从而带来额外开销。
7、dispatch_semaphore_t
信号量的实现,可以实现控制GCD队列任务的最大并发量,类似于NSOperationQueue的maxConcurrentOperationCount属性。
具体用法可以参照iOS系统GCD学习(5):信号量机制