iOS中的锁
前言
写在前面:
临界区:指的是一块对公共资源进行访问的代码,并非一种机制或是算法。
自旋锁:是用于多线程同步的一种锁,线程反复检查锁变量是否可用。由于线程在这一过程中保持执行,因此是一种忙等待。一旦获取了自旋锁,线程会一直保持该锁,直至显式释放自旋锁。 自旋锁避免了进程上下文的调度开销,因此对于线程只会阻塞很短时间的场合是有效的。
互斥锁(Mutex):是一种用于多线程编程中,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制。该目的通过将代码切片成一个一个的临界区而达成。
读写锁:是计算机程序的并发控制的一种同步机制,也称“共享-互斥锁”、多读者-单写者锁) 用于解决多线程对公共资源读写问题。读操作可并发重入,写操作是互斥的。 读写锁通常用互斥锁、条件变量、信号量实现。
信号量(semaphore):是一种更高级的同步机制,互斥锁可以说是semaphore在仅取值0/1时的特例。信号量可以有更多的取值空间,用来实现更加复杂的同步,而不单单是线程间互斥。
条件锁:就是条件变量,当进程的某些资源要求不满足时就进入休眠,也就是锁住了。当资源被分配到了,条件锁打开,进程继续运行。
分布式锁: 分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁
对比自旋锁和互斥锁异同:
相同点:都能保证同一时间只有一个线程访问共享资源。都能保证线程安全。
-
不同点:
互斥锁:如果共享数据已经有其他线程加锁了,线程会进入休眠状态等待锁。一旦被访问的资源被解锁,则等待资源的线程会被唤醒。
自旋锁:如果共享数据已经有其他线程加锁了,线程会以死循环的方式等待锁,一旦被访问的资源被解锁,则等待资源的线程会立即执行。
自旋锁的效率高于互斥锁。
iOS中常用的加锁机制:
- @synchronized
- dispatch_semaphore
- NSLock
- NSRecursiveLock
- NSConditionLock
- NSCondition
- pthread_mutex
- pthread_mutex(recursive)
- OSSpinLock
- osunfairlock
- pthread_rwlockt
- NSDistributedLock
代码
介绍
1. @synchronized
先上代码:
- (void)test {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(self){
NSLog(@"Thread one start ...");
sleep(3);
NSLog(@"Thread one end ...");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(self){
NSLog(@"Thread two end ...");
}
});
}
执行结果:
2018-08-08 14:17:17.927986+0800 TreadTest[88534:2170294] Thread one start ...
2018-08-08 14:17:20.930640+0800 TreadTest[88534:2170294] Thread one end ...
2018-08-08 14:17:20.931061+0800 TreadTest[88534:2170296] Thread two end ...
@synchronized(obj)
指令使用obj作为标识, 只有obj是同一个对象时, 餐能满足互斥条件;
特点:
- synchronized会阻塞调用它的线程
- @synchronized block 在被保护的代码上暗中添加了一个异常处理。同步某对象时如若抛出异常,锁会被释放掉。
- @synchronized 后面需要紧跟一个 OC 对象,它实际上是把这个对象当做锁来使用。这是通过一个哈希表来实现的,OC 在底层使用了一个互斥锁的数组(你可以理解为锁池),通过对对象去哈希值来得到对应的互斥锁。
注意:
Objective-C类自身也是对象所以可以对这些类对象使用synchronized,此时是对整个对象类型进行线程同步。
@synchronized ([self class]) {
}
关于 @synchronized,这儿比你想知道的还要多
2. dispatch_semaphore
代码:
- (void)test {
dispatch_semaphore_t signal = dispatch_semaphore_create(1);
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
int timeSleep = 2;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(signal, timeout);
NSLog(@"SemaphoreTest Thread one start ...");
sleep(timeSleep);
NSLog(@"SemaphoreTest Thread one end ...");
dispatch_semaphore_signal(signal);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(signal, timeout);
NSLog(@"SemaphoreTest Thread two end ...");
dispatch_semaphore_signal(signal);
});
}
timeSleep=2, 输出:
2018-08-08 17:45:49.321698+0800 TreadTest[4121:2420094] SemaphoreTest Thread one start ...
2018-08-08 17:45:51.325003+0800 TreadTest[4121:2420094] SemaphoreTest Thread one end ...
2018-08-08 17:45:51.325509+0800 TreadTest[4121:2420096] SemaphoreTest Thread two end ...
timeSleep=4, 超时输出:
2018-08-08 17:46:45.776442+0800 TreadTest[4159:2421529] SemaphoreTest Thread one start ...
2018-08-08 17:46:48.779212+0800 TreadTest[4159:2421525] SemaphoreTest Thread two end ...
2018-08-08 17:46:49.777714+0800 TreadTest[4159:2421529] SemaphoreTest Thread one end ...
dispatch_semaphore是GCD用来同步的一种方式,相关函数:
-
dispatch_semaphore_create:
dispatch_semaphore_t dispatch_semaphore_create(long value);
传入的参数为long,输出一个dispatch_semaphore_t类型且值为value的信号量,参数value必须大于或等于0,否则dispatch_semaphore_create会返回NULL。
-
dispatch_semaphore_signal
long dispatch_semaphore_signal(dispatch_semaphore_t dsema)
函数会使传入的信号量dsema的值加1;
-
dispatch_semaphore_wait
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
这个函数会使传入的信号量dsema的值减1
如果dsema信号量的值大于0,该函数所处线程就继续执行后边的语句,并且将信号量的值减1;
如果desema的值为0,就阻塞当前线程等待timeout
如果等待的期间desema的值被dispatch_semaphore_signal函数加1了,且该函数(即dispatch_semaphore_wait)所处线程获得了信号量,那么就继续向下执行并将信号量减1。
如果等待期间没有获取到信号量或者信号量的值一直为0,那么等到timeout时,其所处线程自动执行其后语句
注意:
dispatch_semaphore 是信号量,但当信号总量设为 1 时也可以当作锁来。
在没有等待情况出现时,它的性能比 pthread_mutex 还要高,但一旦有等待情况出现时,性能就会下降许多。
相对于 OSSpinLock 来说,它的优势在于等待时不会消耗 CPU 资源
另外, 在设置timeout时, 有两个比较重要的宏,
DISPATCH_TIME_NOW
和DISPATCH_TIME_FOREVER
, 如果一直等待, 可以设置为dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
3. NSLock
NSLock是Cocoa最基本的锁对象, 主要包含四个方法:
-
- (void)lock;
-
- (void)unlock;
-
- (BOOL)tryLock;
-
- (BOOL)lockBeforeDate:(NSDate *)limit;
常规用法:
- (void)test {
NSLock *lock = [NSLock new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@"Thread one start ...");
sleep(3);
NSLog(@"Thread one end ...");
[lock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@"Thread two end ...");
[lock unlock];
});
}
输出:
2018-08-08 18:03:39.690424+0800 TreadTest[4511:2438866] Thread one start ...
2018-08-08 18:03:42.693269+0800 TreadTest[4511:2438866] Thread one end ...
2018-08-08 18:03:42.693767+0800 TreadTest[4511:2438865] Thread two end ...
尝试获取加锁:
- (void)testTry {
NSLock *lock = [NSLock new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
NSLog(@"Thread one start ...");
sleep(3);
NSLog(@"Thread one end ...");
[lock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([lock tryLock]) {
NSLog(@"Thread two get lock ...");
[lock unlock];
} else {
NSLog(@"Thread two fail to get lock ...");
}
});
}
tryLock
会尝试加锁, 如果加锁失败直接返回NO, 不会阻塞线程
输出:
2018-08-08 19:18:04.520605+0800 TreadTest[5962:2484928] Thread one start ...
2018-08-08 19:18:04.520609+0800 TreadTest[5962:2484929] Thread two fail to get lock ...
2018-08-08 19:18:07.523243+0800 TreadTest[5962:2484928] Thread one end ...
lockBeforeDate
会在超时之前一直尝试获取加锁, 如果超则返回NO
获取超时:
- (void)testLockDate {
NSLock *lock = [NSLock new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSLog(@"Thread one start ...");
sleep(3);
NSLog(@"Thread one end ...");
[lock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if ([lock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:5]]) {
NSLog(@"Thread two get lock ...");
[lock unlock];
} else {
NSLog(@"Thread two fail to get lock ...");
}
});
}
输出:
2018-08-08 19:20:27.955699+0800 TreadTest[6043:2488268] Thread one start ...
2018-08-08 19:20:30.956558+0800 TreadTest[6043:2488268] Thread one end ...
2018-08-08 19:20:30.957228+0800 TreadTest[6043:2488272] Thread two get lock ...
4 NSRecursiveLock
- (void)test {
// NSLock *lock = [[NSLock alloc] init];
NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveMethod)(int);
RecursiveMethod = ^(int value) {
[lock lock];
if (value > 0) {
NSLog(@"value = %d", value);
sleep(1);
RecursiveMethod(value - 1);
}
[lock unlock];
};
RecursiveMethod(5);
});
}
输出:
2018-08-08 19:39:20.174989+0800 TreadTest[6500:2511354] value = 5
2018-08-08 19:39:21.180232+0800 TreadTest[6500:2511354] value = 4
2018-08-08 19:39:22.182961+0800 TreadTest[6500:2511354] value = 3
2018-08-08 19:39:23.186836+0800 TreadTest[6500:2511354] value = 2
2018-08-08 19:39:24.190228+0800 TreadTest[6500:2511354] value = 1
NSRecursiveLock
实际上定义的是一个递归锁,这个锁可以被同一线程多次请求,而不会引起死锁。
递归锁会跟踪它被lock的次数。每次成功的lock都必须平衡调用unlock操作。只有所有达到这种平衡,锁最后才能被释放,以供其它线程使用.
如果将NSRecursiveLock
改为NSLock
, 则会引起死锁:
2018-08-08 19:36:25.251656+0800 TreadTest[6422:2507784] value = 5
另外, NSRecursiveLock
提供了另外尝试加锁的方法:
-
- (BOOL)tryLock;
-
- (BOOL)lockBeforeDate:(NSDate *)limit;
5 NSConditionLock
- (void)test {
NSMutableArray *products = [NSMutableArray array];
// NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:2];
NSConditionLock *lock = [[NSConditionLock alloc] init];;
NSInteger HAS_DATA = 1;
NSInteger NO_DATA = 0;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
[lock lockWhenCondition:NO_DATA];
[products addObject:[[NSObject alloc] init]];
NSLog(@"produce a product,总量:%zi",products.count);
[lock unlockWithCondition:HAS_DATA];
sleep(1);
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
NSLog(@"wait for product");
[lock lockWhenCondition:HAS_DATA];
[products removeObjectAtIndex:0];
NSLog(@"custome a product");
[lock unlockWithCondition:NO_DATA];
}
});
}
输出:
2018-08-08 19:52:43.013061+0800 TreadTest[6848:2528186] wait for product
2018-08-08 19:52:43.013075+0800 TreadTest[6848:2528187] produce a product,总量:1
2018-08-08 19:52:43.013229+0800 TreadTest[6848:2528186] custome a product
2018-08-08 19:52:43.013396+0800 TreadTest[6848:2528186] wait for product
2018-08-08 19:52:44.014761+0800 TreadTest[6848:2528187] produce a product,总量:1
2018-08-08 19:52:44.014980+0800 TreadTest[6848:2528186] custome a product
2018-08-08 19:52:44.015118+0800 TreadTest[6848:2528186] wait for product
2018-08-08 19:52:45.018647+0800 TreadTest[6848:2528187] produce a product,总量:1
2018-08-08 19:52:45.018906+0800 TreadTest[6848:2528186] custome a product
NSConditionLock有一个很重要的属性:
@property (readonly) NSInteger condition;
执行[lock lockWhenCondition:value]
时, 只有value== lock.condition
时, 才能获得锁;
执行[lock unlockWithCondition:value]
时, 就会把lock.condition设置为value, 以供其他线程按照该条件获取锁.
NSConditionLock
可以接受不同的条件, 进而影响不同线程之间的加锁状态.
6 NSCondition
- (void)test {
NSCondition *condition = [[NSCondition alloc] init];
NSMutableArray *products = [NSMutableArray array];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
[condition lock];
NSLog(@"custome lock");
if ([products count] == 0) {
NSLog(@"wait for product");
[condition wait];
}
[products removeObjectAtIndex:0];
NSLog(@"custome a product");
[condition unlock];
NSLog(@"custome unlock");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
while (1) {
[condition lock];
NSLog(@"factory lock");
[products addObject:[[NSObject alloc] init]];
NSLog(@"produce a product,总量:%zi",products.count);
[condition signal];
NSLog(@"signal a product,总量:%zi",products.count);
[condition unlock];
NSLog(@"factory unlock");
sleep(1);
}
});
}
输出:
2018-08-08 20:18:56.220689+0800 TreadTest[7570:2561183] custome lock
2018-08-08 20:18:56.220809+0800 TreadTest[7570:2561183] wait for product
2018-08-08 20:18:56.220908+0800 TreadTest[7570:2561187] factory lock
2018-08-08 20:18:56.221005+0800 TreadTest[7570:2561187] produce a product,总量:1
2018-08-08 20:18:56.221779+0800 TreadTest[7570:2561187] signal a product,总量:1
2018-08-08 20:18:56.221865+0800 TreadTest[7570:2561187] factory unlock
2018-08-08 20:18:56.221870+0800 TreadTest[7570:2561183] custome a product
2018-08-08 20:18:56.221985+0800 TreadTest[7570:2561183] custome unlock
2018-08-08 20:18:56.222044+0800 TreadTest[7570:2561183] custome lock
2018-08-08 20:18:56.222296+0800 TreadTest[7570:2561183] wait for product
NSCondition是一种最基本的条件锁。手动控制线程wait和signal。
[condition lock]
一般用于多线程同时访问、修改同一个数据源,保证在同一时间内数据源只被访问、修改一次,其他线程的命令需要在lock 外等待,只到unlock ,才可访问;
[condition unlock]
与lock 同时使用
[condition wait]
让当前线程处于等待状态, 但是condition并未解锁
[condition signal]
CPU发信号告诉线程不用在等待,可以继续执行, 但是需要当前线程unlock之后, 其他wait线程才能继续
7 pthread_mutex
递归锁
- (void)test {
__block pthread_mutex_t theLock;
pthread_mutex_init(&theLock, NULL);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&theLock);
NSLog(@"需要线程同步的操作1 开始");
sleep(3);
NSLog(@"需要线程同步的操作1 结束");
pthread_mutex_unlock(&theLock);
}); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
pthread_mutex_lock(&theLock);
NSLog(@"需要线程同步的操作2");
pthread_mutex_unlock(&theLock);
});
}
输出:
2018-08-08 20:29:14.289222+0800 TreadTest[7848:2573203] 需要线程同步的操作1 开始
2018-08-08 20:29:17.294626+0800 TreadTest[7848:2573203] 需要线程同步的操作1 结束
2018-08-08 20:29:17.295094+0800 TreadTest[7848:2573206] 需要线程同步的操作2
pthread_mutex
需要引入#import
, 具体方法:
-
int pthread_mutex_init(pthread_mutex_t * __restrict, const pthread_mutexattr_t * _Nullable __restrict);
创建一个锁
-
int pthread_mutex_lock(pthread_mutex_t *);
加锁, 当锁已经在使用的时候,挂起等待
-
int pthread_mutex_trylock(pthread_mutex_t *);
尝试加锁,当锁已经在使用的时候,返回为EBUSY,不会挂起等待
-
int pthread_mutex_unlock(pthread_mutex_t *);
解锁
-
int pthread_mutexattr_destroy(pthread_mutexattr_t *)
销毁锁, 使用完之后记得销毁.
8 pthread_mutex(recursive)
- (void)test {
__block pthread_mutex_t theLock;
//pthread_mutex_init(&theLock, NULL);
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&theLock, &attr);
pthread_mutexattr_destroy(&attr);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
static void (^RecursiveMethod)(int);
RecursiveMethod = ^(int value) {
pthread_mutex_lock(&theLock);
if (value > 0) {
NSLog(@"value = %d", value);
sleep(1);
RecursiveMethod(value - 1);
}
pthread_mutex_unlock(&theLock);
};
RecursiveMethod(5);
});
}
输出:
2018-08-08 21:31:16.271338+0800 TreadTest[12858:2652044] value = 5
2018-08-08 21:31:17.276607+0800 TreadTest[12858:2652044] value = 4
2018-08-08 21:31:18.281560+0800 TreadTest[12858:2652044] value = 3
2018-08-08 21:31:19.283039+0800 TreadTest[12858:2652044] value = 2
2018-08-08 21:31:20.284007+0800 TreadTest[12858:2652044] value = 1
pthread_mutex为了防止在递归的情况下出现死锁而出现的递归锁。
作用和NSRecursiveLock递归锁类似。
如果使用pthread_mutex_init(&theLock, NULL);初始化锁的话,上面的代码会出现死锁现象。
9 OSSpinLock
- (void)test {
__block OSSpinLock theLock = OS_SPINLOCK_INIT;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSSpinLockLock(&theLock);
NSLog(@"需要线程同步的操作1 开始");
sleep(3);
NSLog(@"需要线程同步的操作1 结束");
OSSpinLockUnlock(&theLock);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OSSpinLockLock(&theLock);
sleep(1);
NSLog(@"需要线程同步的操作2");
OSSpinLockUnlock(&theLock);
});
}
输出:
2018-08-08 21:38:06.443968+0800 TreadTest[13109:2660908] 需要线程同步的操作1 开始
2018-08-08 21:38:09.448119+0800 TreadTest[13109:2660908] 需要线程同步的操作1 结束
2018-08-08 21:38:10.504616+0800 TreadTest[13109:2660909] 需要线程同步的操作2
需要导入#import
OSSpinLock 自旋锁,性能最高的锁;原理很简单,就是一直 do while 忙等。它的缺点是当等待时会消耗大量 CPU 资源,所以它不适用于较长时间的任务
自旋锁不会让等待的进入睡眠状态
不再安全的 OSSpinLock中说明了OSSpinLock已经不再安全,在iOS10.0中已经废弃。
10 os_unfair_lock
- (void)test {
__block os_unfair_lock unfairLock = OS_UNFAIR_LOCK_INIT;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
os_unfair_lock_lock(&unfairLock);
NSLog(@"需要线程同步的操作1 开始");
sleep(3);
NSLog(@"需要线程同步的操作1 结束");
os_unfair_lock_unlock(&unfairLock);
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (os_unfair_lock_trylock(&unfairLock)) {
sleep(1);
NSLog(@"需要线程同步的操作2");
os_unfair_lock_unlock(&unfairLock);
} else {
NSLog(@"lock fail");
}
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
os_unfair_lock_lock(&unfairLock);
NSLog(@"需要线程同步的操作3 开始");
sleep(3);
NSLog(@"需要线程同步的操作3 结束");
os_unfair_lock_unlock(&unfairLock);
});
}
输出:
2018-08-09 11:36:37.785061+0800 TreadTest[23389:2849366] 需要线程同步的操作1 开始
2018-08-09 11:36:37.785102+0800 TreadTest[23389:2849365] lock fail
2018-08-09 11:36:40.790521+0800 TreadTest[23389:2849366] 需要线程同步的操作1 结束
2018-08-09 11:36:40.791035+0800 TreadTest[23389:2849364] 需要线程同步的操作3 开始
2018-08-09 11:36:43.795681+0800 TreadTest[23389:2849364] 需要线程同步的操作3 结束
输出结果可能不一定, 取决于那个线程先拿到锁
需要导入#import
自旋锁已经不在安全,然后苹果又整出来个 os_unfair_lock_t ,这个锁解决了优先级反转问题。
底层调用看,为互斥锁,等待os_unfair_lock锁的线程会处于休眠,而并非忙等,等不到锁就休眠(ios10以上使用)
os_unfair_lock_t unfairLock;
unfairLock = &(OS_UNFAIR_LOCK_INIT);
os_unfair_lock_lock(unfairLock);
os_unfair_lock_unlock(unfairLock)
11 pthread_rwlock 读写锁
百度百科:读写锁实际是一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。这种锁相对于自旋锁而言,能提高并发性,因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者
测试读取操作:
- (void)testRead {
__block pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock,NULL);
int i = 4;
while (i>0) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
NSLog(@"线程0:加锁");
pthread_rwlock_rdlock(&rwlock);
NSLog(@"线程0:读");
pthread_rwlock_unlock(&rwlock);
NSLog(@"线程0:解锁");
});
i--;
}
}
输出:
2018-08-09 14:18:13.626237+0800 TreadTest[32346:2991544] 线程0:加锁
2018-08-09 14:18:13.626241+0800 TreadTest[32346:2991546] 线程0:加锁
2018-08-09 14:18:13.626320+0800 TreadTest[32346:2991545] 线程0:加锁
2018-08-09 14:18:13.626320+0800 TreadTest[32346:2991547] 线程0:加锁
2018-08-09 14:18:13.626779+0800 TreadTest[32346:2991544] 线程0:读
2018-08-09 14:18:13.626792+0800 TreadTest[32346:2991546] 线程0:读
2018-08-09 14:18:13.626973+0800 TreadTest[32346:2991545] 线程0:读
2018-08-09 14:18:13.627002+0800 TreadTest[32346:2991547] 线程0:读
2018-08-09 14:18:13.627102+0800 TreadTest[32346:2991544] 线程0:解锁
2018-08-09 14:18:13.627126+0800 TreadTest[32346:2991546] 线程0:解锁
2018-08-09 14:18:13.627221+0800 TreadTest[32346:2991545] 线程0:解锁
2018-08-09 14:18:13.627241+0800 TreadTest[32346:2991547] 线程0:解锁
不同线程之间并没有阻塞, 即没有等待其他线程解锁就可以进行读取
写操作:
- (void)testWrite {
__block pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock,NULL);
int j = 4;
while (j>0) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
sleep(1);
NSLog(@"线程1:加锁");
pthread_rwlock_wrlock(&rwlock);
NSLog(@"线程1:写");
pthread_rwlock_unlock(&rwlock);
NSLog(@"线程1:解锁");
});
j--;
}
}
输出:
2018-08-09 14:24:48.828117+0800 TreadTest[32581:3000922] 线程1:加锁
2018-08-09 14:24:48.828117+0800 TreadTest[32581:3000937] 线程1:加锁
2018-08-09 14:24:48.828140+0800 TreadTest[32581:3000920] 线程1:加锁
2018-08-09 14:24:48.828178+0800 TreadTest[32581:3000921] 线程1:加锁
2018-08-09 14:24:48.828555+0800 TreadTest[32581:3000922] 线程1:写
2018-08-09 14:24:48.829907+0800 TreadTest[32581:3000922] 线程1:解锁
2018-08-09 14:24:48.829908+0800 TreadTest[32581:3000937] 线程1:写
2018-08-09 14:24:48.830625+0800 TreadTest[32581:3000937] 线程1:解锁
2018-08-09 14:24:48.830638+0800 TreadTest[32581:3000920] 线程1:写
2018-08-09 14:24:48.830981+0800 TreadTest[32581:3000920] 线程1:解锁
2018-08-09 14:24:48.830985+0800 TreadTest[32581:3000921] 线程1:写
2018-08-09 14:24:48.831438+0800 TreadTest[32581:3000921] 线程1:解锁
输出结果显示, 线程的写操作需要等待解锁(sleep时间不同可能造成输出顺序不同, 应该是和线程的时间片有关)
12 NSDistributedLock 分布式锁
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,在这种情况下,便需要使用到分布式锁
- 可以用来限制对某些共享资源的访问,比如一个文件。
- 没有实现NSLocking协议,所有没有lock方法。一个lock方法将会阻塞线程的执行,并要求系统以预定的速度轮询锁。
- 它提供了一个tryLock方法,并让你决定是否轮询。访问结束后,可以通过调用unlock方法来释放它
处理多个进程或多个程序之间互斥问题 - 一个获取锁的进程或程序在释放锁之前挂掉,锁不会被释放,可以通过breakLock方法解锁
Mac下NSDistributedLock同步性能
参考:
iOS开发中的11种锁以及性能对比
iOS中保证线程安全的几种方式与性能对比
OC--各种线程锁
iOS底层原理-多线程
Lock in iOS
iOS 锁的简单实现与总结
iOS-线程安全
互斥锁的实现