多线程编程中,当多个线程访问同一块资源的时候,处理不当,可能会造成数据错乱,数据安全性等问题。解决这个问题就需要保证在访问这块资源的时候只有一个线程,其他想访问的排队等候。所以就要进行锁定,所以使用锁。
按照效率高->低依次看一下
OSSpinLock自旋锁
《不再安全的OSSpinLock》中讲到:如果一个低优先级的线程获得锁并访问共享资源,这时一个高优先级的线程也尝试获得这个锁,它会处于 spin lock 的忙等状态从而占用大量 CPU。此时低优先级线程无法与高优先级线程争夺 CPU 时间,从而导致任务迟迟完不成、无法释放 lock。
所以在我们使用的时候,要将线程置于同一优先级。
-(void)OSSpinLock
{
__block OSSpinLock oslock = OS_SPINLOCK_INIT;
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"locking---1");
OSSpinLockLock(&oslock);
sleep(5);
NSLog(@"线程1执行");
OSSpinLockUnlock(&oslock);
NSLog(@"unlock---1");
});
dispatch_async(queue, ^{
NSLog(@"locking---2");
OSSpinLockLock(&oslock);
NSLog(@"线程2执行");
OSSpinLockUnlock(&oslock);
NSLog(@"unlock---2");
});
}
运行结果
14:12:02.656 [2682:130985] locking---1
14:12:02.656 [2682:130987] locking---2
14:12:07.714 [2682:130985] 线程1执行
14:12:07.714 [2682:130985] unlock---1
14:12:07.715 [2682:130987] 线程2执行
14:12:07.715 [2682:130987] unlock---2
参数说明
OS_SPINLOCK_INIT:默认==0,在lock状态下>0,unlock状态下<0
OSSpinLockLock(&osLock):加锁,参数为osLock地址
OSSpinLockUnlock(&osLock):解锁,参数为osLock地址
OSSpinLockTry(&osLock):尝试加锁,如果可以加锁,则立即加锁并返回YES,如果不可以,则返回NO
dispatch_semaphore
通过控制信号量的值,来控制执行哪个线程。
-(void)semaphore
{
dispatch_semaphore_t sema = dispatch_semaphore_create(1);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"wait---1");
dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC));
NSLog(@"线程1执行");
dispatch_semaphore_signal(sema);
NSLog(@"signal---1");
});
dispatch_async(queue, ^{
NSLog(@"wait---2");
dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, 3.0 * NSEC_PER_SEC));
NSLog(@"线程2执行");
dispatch_semaphore_signal(sema);
NSLog(@"signal---2");
});
}
dispatch_semaphore_create(x);
当x > 0,并不会发生线程阻塞,而是直接执行了线程1和线程2(看运行时间)。
11:36:29.755 [1717:74601] wait---1
11:36:29.755 [1717:74600] wait---2
11:36:29.756 [1717:74601] 线程1执行
11:36:29.757 [1717:74601] signal---1
11:36:29.757 [1717:74600] 线程2执行
11:36:29.757 [1717:74600] signal---2
当x == 0时,会造成线程阻塞,此时,设置的dispatch_time才会有效
11:39:11.958 [1766:76560] wait---1
11:39:11.958 [1766:76562] wait---2
11:39:14.965 [1766:76562] 线程2执行
11:39:14.965 [1766:76560] 线程1执行
11:39:14.965 [1766:76562] signal---2
11:39:14.966 [1766:76560] signal---1
pthreadMutex互斥锁
是用了pthread中的方法,pthread 是 POSIX 多线程开发框架,是跨平台的 C 语言框架。
#import
-(void)pthreadMutex
{
static pthread_mutex_t pthreadLock;
pthread_mutex_init(&pthreadLock, NULL);
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"lock---1");
pthread_mutex_lock(&pthreadLock);
NSLog(@"线程1执行");
pthread_mutex_unlock(&pthreadLock);
NSLog(@"unlock---1");
});
dispatch_async(queue, ^{
NSLog(@"lock---2");
pthread_mutex_lock(&pthreadLock);
NSLog(@"线程2执行");
pthread_mutex_unlock(&pthreadLock);
NSLog(@"unlock---2");
});
}
运行结果:一个线程释放之后,另一个线程立即执行
11:41:55.080 [1810:78248] lock---1
11:41:55.080 [1810:78264] lock---2
11:41:55.081 [1810:78248] 线程1执行
11:41:55.082 [1810:78248] unlock---1
11:41:55.082 [1810:78264] 线程2执行
11:41:55.082 [1810:78264] unlock---2
pthread的递归锁
-(void)pthreadMutexattr
{
static pthread_mutex_t pLock;
pthread_mutexattr_t attr;
//初始化attr并且给它赋予默认
pthread_mutexattr_init(&attr);
//设置锁类型为递归锁
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&pLock, &attr);
//销毁一个属性对象,在重新进行初始化之前该结构不能重新使用
pthread_mutexattr_destroy(&attr);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^RecursiveBlock)(int);
RecursiveBlock = ^(int value){
pthread_mutex_lock(&pLock);
if (value > 0) {
NSLog(@"value: %d", value);
RecursiveBlock(value - 1);
}
pthread_mutex_unlock(&pLock);
};
RecursiveBlock(5);
});
}
NSLock
-(void)Lock
{
NSLock *lock = [[NSLock alloc]init];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
NSLog(@"locking---1");
[lock lock];
NSLog(@"线程1执行");
// [NSThread sleepForTimeInterval:5];
[lock unlock];
NSLog(@"unlocked---1");
});
dispatch_async(queue, ^{
//尝试在指定时间内加锁
NSLog(@"locking---2");
BOOL isLocked = [lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:2]];
if (isLocked) {
NSLog(@"线程2执行");
[lock unlock];
NSLog(@"unlocked---2");
}else{
NSLog(@"lockfailed");
}
});
}
运行结果
11:52:10.626 [1904:83210] locking---1
11:52:10.626 [1904:83212] locking---2
11:52:10.626 [1904:83210] 线程1执行
11:52:10.628 [1904:83210] unlocked---1
11:52:10.628 [1904:83212] 线程2执行
11:52:10.629 [1904:83212] unlocked---2
[lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:2]];
尝试在指定时间内加锁
如果放开[NSThread sleepForTimeInterval:5];
这个方法,则尝试加锁失败,运行结果为
11:53:15.561 [1933:84049] locking---1
11:53:15.561 [1933:84047] locking---2
11:53:15.561 [1933:84049] 线程1执行
11:53:17.614 [1933:84047] lockfailed
11:53:20.637 [1933:84049] unlocked---1
Condition锁
-(void)Condition
{
NSCondition *condition = [[NSCondition alloc]init];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
[condition lock];
NSLog(@"locked---1");
//让一个线程等待一定时间
// [condition waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:2]];
[condition wait];
NSLog(@"线程1执行");
[condition unlock];
NSLog(@"unlocked---1");
});
dispatch_async(queue, ^{
[condition lock];
NSLog(@"locked---2");
[condition wait];
NSLog(@"线程2执行");
[condition unlock];
NSLog(@"unlocked---2");
});
dispatch_async(queue, ^{
sleep(2);
//唤醒所有等待线程
NSLog(@"唤醒所有线程");
[condition broadcast];
//唤醒一个等待线程
// NSLog(@"等待唤醒线程");
// [condition signal];
});
}
wait:等待
signal:唤醒一个线程
broadcast:唤醒所有等待线程
waitUntilDate:让一个线程等待一定时间
NSRecursiveLock递归锁
-(void)RecursiveLock
{
NSLock *rLock = [[NSLock alloc]init];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^RecursiveBlock)(int);
RecursiveBlock = ^(int value) {
[rLock lock];
if (value > 0) {
NSLog(@"线程%d", value);
RecursiveBlock(value - 1);
}
[rLock unlock];
};
RecursiveBlock(4);
});
}
Synchronized条件锁
-(void)Synchronized
{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
@synchronized (self) {
[NSThread sleepForTimeInterval:2];
NSLog(@"线程1");
}
});
dispatch_async(queue, ^{
@synchronized (self) {
NSLog(@"线程2");
}
});
}
NSConditionLock条件锁
最大的特点:可以通过控制condition的值来控制执行顺序,类似添加依赖。
-(void)ConditionLock
{
NSConditionLock *conLock = [[NSConditionLock alloc]initWithCondition:0];
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
if ([conLock tryLockWhenCondition:0]) {
NSLog(@"线程1执行");
[conLock unlockWithCondition:1];
}else{
NSLog(@"lockfailed");
}
});
dispatch_async(queue, ^{
[conLock lockWhenCondition:2];
NSLog(@"线程2执行");
[conLock unlockWithCondition:3];
});
dispatch_async(queue, ^{
[conLock lockWhenCondition:1];
NSLog(@"线程3执行");
[conLock unlockWithCondition:2];
});
}
在使用[conLock lockWhenCondition:1];
以及[conLock unlockWithCondition:2];
时,其实就是对condition做了修改,线程执行的时候会首先去找匹配的condition,运行结果
12:10:57.222 [2143:93635] 线程1执行
12:10:57.222 [2143:93636] 线程3执行
12:10:57.222 [2143:93651] 线程2执行