为了解决线程同步问题,需要使用线程锁
@synchronized. 加锁的对象需要是同一个对象
NSLock 对象锁。 多次lock死锁
NSRecursiveLock 递归锁。 场景限制
NSConditionLock 条件锁。
pthread_mutex (C语言)互斥锁 linux 底层
dispatch_semaphore (GCD)。 信号量
OSSpinLock (不建议使用)
性能对比图:
@synchronized 关键字加锁 互斥锁,性能较差不推荐使用
@synchronized(OC对象,一般使用self){
需要加锁的代码
}
注意:
a.加锁的代码尽量少
b.添加的OC对象必须在多个线程中都是同一对象
c.优点是不需要显示的创建锁对象,便可以实现锁的机制
d.@synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。所以如果不想让隐式的异常处理例程带来额外的开销,你可以考虑使用锁对象。
NSLock 互斥锁 不能多次调用lock方法,会造成死锁
_mutexLock = [ [NSLock alloc] init];
在需要加锁的地方
// 加锁
[_mutexLock lock];
操作代码
。。。。。
// 解锁
[_mutexLock unlock];
NSRecursiveLock 递归锁
在递归中使用NSLock 多次调用lock ,会阻塞线程
换成NSRecursiveLock 便可以轻松解决,不过使用场景比较局限。
_rsLock = [[NSRecursiveLock alloc] init];
同NSLock 加锁,解锁
NSConditionLock 条件锁
//主线程中
NSConditionLock *theLock = [[NSConditionLock alloc] init];
//线程1
dispatch_async(self.concurrentQueue, ^{
for (int i=0;i<=3;i++)
{
[theLock lock];
NSLog(@"thread1:%d",i);
sleep(1);
[theLock unlockWithCondition:i];
}
});
//线程2
dispatch_async(self.concurrentQueue, ^{
[theLock lockWhenCondition:2];
NSLog(@"thread2");
[theLock unlock];
});
在线程1中的加锁使用了lock,是不需要条件的,所以顺利的就锁住了。
unlockWithCondition:在开锁的同时设置了一个整型的条件 2 。
线程2则需要一把被标识为2的钥匙,所以当线程1循环到 i = 2 时,线程2的任务才执行。
NSConditionLock也跟其它的锁一样,是需要lock与unlock对应的,只是lock,lockWhenCondition:与unlock,unlockWithCondition:是可以随意组合的,当然这是与你的需求相关的。
pthread_mutex 互斥锁(引入头文件#import
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
pthread_mutex_lock(&mutex);
NSLog(@"任务1");
sleep(2);
pthread_mutex_unlock(&mutex);
pthread_mutex_destroy(&mutex); //释放该锁的数据结构
dispatch_semaphore 信号量实现加锁(GCD 中提供的一种信号机制,但是有信号量与互斥锁是有区别的)
// 创建信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//线程1
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"任务1");
sleep(10);
dispatch_semaphore_signal(semaphore);
});
OSSpinLock 效率最高,但是不推荐使用了已经
//创建锁
_pinLock = OS_SPINLOCK_INIT;
//加锁
OSSpinLockLock(&_pinLock);
//解锁
OSSpinLockUnlock(&_pinLock);
总结
@synchronized:适用线程不多,任务量不大的多线程加锁
NSLock:其实NSLock并没有想象中的那么差,不知道大家为什么不推荐使用
dispatch_semaphore_t:使用信号来做加锁,性能提升显著
NSCondition:使用其做多线程之间的通信调用不是线程安全的
NSConditionLock:单纯加锁性能非常低,比NSLock低很多,但是可以用来做多线程处理不同任务的通信调用
NSRecursiveLock:递归锁的性能出奇的高,但是只能作为递归使用,所以限制了使用场景
NSDistributedLock:因为是MAC开发的,就不讨论了
POSIX(pthread_mutex):底层的api,复杂的多线程处理建议使用,并且可以封装自己的多线程
OSSpinLock:性能也非常高,可惜出现了线程问题
dispatch_barrier_async/dispatch_barrier_sync:测试中发现dispatch_barrier_sync比dispatch_barrier_async性能要高,真是大出意外
Q:自旋和互斥的对比
A:相同点:都可以解决线程同步问题
不同点:
互斥锁会在访问被加锁数据时,会休眠等待,当数据解锁,互斥锁会被唤醒。
自旋锁遇到被加锁数据时,会进入死循环等待,当数据解锁,自旋锁马上访问。
自旋锁比互斥锁效率高!
Q:以上锁注意事项
Q:用C/OC/C++,任选其一,实现自旋或互斥?