多线程的锁大致可分为两大类:互斥锁、自旋锁;也可以分为三类:互斥锁、自旋锁、读写锁。
一、互斥锁:互斥+同步(强调的是顺序)
为了保证共享数据操作的安全完整性。对于对象来讲保证每一时刻,只能有一个线程访问该对象,其可以保护单线程代码。
- 递归锁 :NSRecursiveLock,可递归,但是无法多线程递归
- 不递归 : NSLock,不可以递归加锁
二、自旋锁:互斥 + 忙等
三、读写锁:
特点:
- 同一时间,只能有一个线程进行写的操作(写互斥)
- 同一时间,允许多个线程进行读操作
- 同一时间,不允许既有写的操作,又有读的操作(读写互斥)
- (void)psy_safeWrite:(NSString *)result time:(int)time{
dispatch_barrier_async(self.psy_queue, ^{ // 在异步线程中加入栅栏函数
sleep(time);
self.mDict[@"pansy"] = result;
NSLog(@"写入: %@ -- %@",result,[NSThread currentThread]);
});
}
- (NSString *)psy_safeRead:(int)time{
__block NSString *result = @"还未赋值";
dispatch_sync(self.psy_queue, ^{ // 加同步函数
result = self.mDict[@"pansy"];
NSLog(@"读取: %@ -- %@",result,[NSThread currentThread]);
});
return result;
}
OK,下面就讲讲iOS的锁,大佬勿喷。
-
OSSpinLock
OSSpinLock 是一种自旋锁,会出现do...while忙等,缺点是在等待过程中会损耗CPU资源,因为每一条线程会被分配10~100ms时间片,操作系统线程调度采用时间片轮转调度算法(Round Robin,RR),它会占用线程时间片,也就是CPU资源,而不是线程休眠挂起的方式。而且会出现线程优先级翻转可能性,即低优先级线程先访问资源,高优先级等待低优先级的情况(在访问资源和执行任务的时候),已经不安全,iOS10开始以后就被os_unfair_lock
替代了
相关函数:
- OSSpinLock psy_spinlock = OS_SPINLOCK_INIT;
- OSSpinLockLock(&psy_spinlock);
- OSSpinLockUnlock(&psy_spinlock);
案例
__block OSSpinLock psy_spinlock = OS_SPINLOCK_INIT;
dispatch_queue_t concurrentQueue = dispatch_queue_create("psybiu", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
OSSpinLockLock(& psy_spinlock);
printf("操作1 开始\n");
sleep(3);
printf("操作1 结束\n");
OSSpinLockUnlock(& psy_spinlock);
});
dispatch_async(concurrentQueue, ^{
OSSpinLockLock(& psy_spinlock);
sleep(1);
printf("操作2\n");
OSSpinLockUnlock(& psy_spinlock);
});
输出结果为:
操作1 开始
操作1 结束
操作2
-
os_unfair_lock
要使用,需要导入头文件#import <os/lock.h>
os_unfair_lock psy_unfairlock = OS_UNFAIR_LOCK_INIT;
os_unfair_lock_lock(&psy_unfairlock);
os_unfair_lock_unlock(&psy_unfairlock);
案例
__block os_unfair_lock psy_unfairlock = OS_UNFAIR_LOCK_INIT;
dispatch_queue_t concurrentQueue = dispatch_queue_create("psybiu", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
os_unfair_lock_lock(&psy_unfairlock);
printf("操作1 开始\n");
sleep(3);
printf("操作1 结束\n");
os_unfair_lock_unlock(&psy_unfairlock);
});
dispatch_async(concurrentQueue, ^{
sleep(1);
os_unfair_lock_lock(&psy_unfairlock);
printf("操作2\n");
os_unfair_lock_unlock(&psy_unfairlock);
});
输出结果为:
操作1 开始
操作1 结束
操作2
-
dispatch_semaphore_t
GCD的信号量,当信号量是1的时候就可以当锁来使用。主要的函数如下,wait和signal是成对配合出现的,一个是等待信号量减1,一个是信号量加1。
dispatch_semaphore_t psy_sem = dispatch_semaphore_create(1);
dispatch_semaphore_wait(psy_sem, DISPATCH_TIME_FOREVER);
dispatch_semaphore_signal(psy_sem);
案例
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC);
for (NSInteger k = 0; k < 4; k++) {
dispatch_semaphore_wait(semaphore, timeout);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < 10; i++) {
printf("k:%ld( %ld )--- ",k,i);
}
dispatch_semaphore_signal(semaphore);
});
}
输出结果为:
k:0( 0 )--- k:0( 1 )--- k:0( 2 )--- k:0( 3 )--- k:0( 4 )--- k:0( 5 )--- k:0( 6 )--- k:0( 7 )--- k:0( 8 )--- k:0( 9 )--- k:1( 0 )--- k:1( 1 )--- k:1( 2 )--- k:1( 3 )--- k:1( 4 )--- k:1( 5 )--- k:1( 6 )--- k:1( 7 )--- k:1( 8 )--- k:1( 9 )--- k:2( 0 )--- k:2( 1 )--- k:2( 2 )--- k:2( 3 )--- k:2( 4 )--- k:2( 5 )--- k:2( 6 )--- k:2( 7 )--- k:2( 8 )--- k:2( 9 )--- k:3( 0 )--- k:3( 1 )--- k:3( 2 )--- k:3( 3 )--- k:3( 4 )--- k:3( 5 )--- k:3( 6 )--- k:3( 7 )--- k:3( 8 )--- k:3( 9 )---
-
pthread_mutex_t
要使用需要导入头文件#import <pthread/pthread.h>
初始化一个pthread_mutex_t互斥锁,可以PTHREAD_MUTEX_INITIALIZER静态初始化,也可以int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)动态初始化
操作:
pthread_mutex_lock(&psy_metext); 加锁
pthread_mutex_unlock(&psy_metext); 解锁
pthread_mutex_trylock(&psy_metext); 这个方法与pthread_mutex_lock类似,不一样的是trylock在加锁时如果锁已经被占用,则返回EBUSY而不是挂起等待。
案例
__block pthread_mutex_t customLock;
pthread_mutex_init(&customLock, NULL);
dispatch_queue_t concurrentQueue = dispatch_queue_create("psybiu", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(concurrentQueue, ^{
pthread_mutex_lock(&customLock);
printf("操作1 开始");
sleep(3);
printf("操作1 结束");
pthread_mutex_unlock(&customLock);
});
dispatch_async(concurrentQueue, ^{
sleep(1);
pthread_mutex_lock(&customLock);
printf("操作2");
pthread_mutex_unlock(&customLock);
});
输出结果为:
操作1 开始
操作1 结束
操作2
-
pthread_mutex_recursive
pthread_mutex_t psy_metext_recurive;
pthread_mutexattr_t attr;
pthread_mutexattr_init (&attr);
pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init (&psy_metext_recurive, &attr);
pthread_mutex_lock(&psy_metext_recurive);
pthread_mutex_unlock(&psy_metext_recurive);
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
-
NSLock
其属于Foundation框架,但是这个框架没有开源,可以看一下其头文件源码如下<NSLock源码:
>:
NSLock *psy_lock = [[NSLock alloc] init]; 创建锁对象
[psy_lock lock]; 加锁
[psy_lock unlock]; 解锁
NSLock源码:
@interface NSLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
实例
NSLock *lock = [[NSLock alloc] init];
for (int i= 0; i<10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^testMethod)(int);
testMethod = ^(int value){
if (value > 0) {
printf("current value = %d\n",value);
testMethod(value - 1);
}
};
[lock lock];
testMethod(10); // 对关键业务代码加锁
[lock unlock];
});
}
其输出结果是输出10次的:
current value = 10
current value = 9
current value = 8
current value = 7
current value = 6
current value = 5
current value = 4
current value = 3
current value = 2
current value = 1
.................
虽然OC的Foundation框架没有开源,但是可以查看swift的Foundation框架,从swift查看NSLock底层源码实现,底层封装的是pthread_mutex_t,
NSLock *psy_lock = [[NSLock alloc] init]; -->底层就是进行了pthread_mutex_init(mutex, nil);
[psy_lock lock]; --> 实际上底层是调用了 pthread_mutex_lock
[psy_lock unlock]; --> pthread_mutex_unlock
-
NSRecursiveLock
底层也是封装的pthread_mutex_t
NSRecursiveLock *psy_recursiveLock = [NSRecursiveLock new];
[psy_recursiveLock lock]; --> 实际上底层是调用了pthread_mutex_lock
[psy_recursiveLock unlock]; -->pthread_mutex_unlock
源码
@interface NSRecursiveLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
案例
NSRecursiveLock *recursiveLock = [[NSRecursiveLock alloc] init];
// for (int i= 0; i<10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^testMethod)(int);
testMethod = ^(int value){
[self.recursiveLock lock];
if (value > 0) {
printf("current value = %d\n",value);
testMethod(value - 1);
}
[self.recursiveLock unlock];
};
testMethod(10);
[lock unlock];
});
// }
输出结果为:
current value = 10
current value = 9
current value = 8
current value = 7
current value = 6
current value = 5
current value = 4
current value = 3
current value = 2
current value = 1
查看swift的Foundation框架,与NSLock的底层实现非常相似,不一样的就是NSRecursiveLock对pthread_mutexattr_t属性进行了配置
internal var mutex = _RecursiveMutexPointer.allocate(capacity: 1)
withUnsafeMutablePointer(to: &attrib) { attrs in
pthread_mutexattr_init(attrs)
// 设置了type为PTHREAD_MUTEX_RECURSIVE
pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))
pthread_mutex_init(mutex, attrs)
}
-
NSCondition
NSCondition *psy_condition = [NSCondition new];
[psy_condition lock];
[psy_condition unlock];
源码
@interface NSCondition : NSObject <NSLocking> {
@private
void *_priv;
}
- (void)wait;
- (BOOL)waitUntilDate:(NSDate *)limit;
- (void)signal;
- (void)broadcast;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
案例
生产与消费问题,当生产为0,不允许消费,直到生产 >=1 才允许消费。
self.ticketCount = 0;
_testCondition = [[NSCondition alloc] init];
//创建生产-消费者
for (int i = 0; i < 50; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[_testCondition lock]; // 操作的多线程影响
self.ticketCount = self.ticketCount + 1;
NSLog(@"生产一个 现有 count %zd",self.ticketCount);
[_testCondition signal]; // 信号
[_testCondition unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[_testCondition lock]; // 操作的多线程影响
if (self.ticketCount == 0) {
NSLog(@"等待 count %zd",self.ticketCount);
[_testCondition wait];
}
//注意消费行为,要在等待条件判断之后
self.ticketCount -= 1;
NSLog(@"消费一个 还剩 count %zd ",self.ticketCount);
[_testCondition unlock];
});
}
其输出结果:比较长可自己运行看看
在swift的Foundation中其本质也是封装的pthread_mutex_t,[[NSCondition alloc] init],实际上底层做了两件事:
- pthread_mutex_init(mutex, nil)
- pthread_cond_init(cond, nil)
比NSLock多了一个pthread_cond_init,具体源码如下:
public override init() {
pthread_mutex_init(mutex, nil)
pthread_cond_init(cond, nil)
}
open func lock() {
pthread_mutex_lock(mutex)
}
open func unlock() {
pthread_mutex_unlock(mutex)
}
open func wait() {
pthread_cond_wait(cond, mutex)
}
open func wait(until limit: Date) -> Bool {
guard var timeout = timeSpecFrom(date: limit) else {
return false
}
return pthread_cond_timedwait(cond, mutex, &timeout) == 0
}
open func signal() {
pthread_cond_signal(cond)
}
open func broadcast() {
pthread_cond_broadcast(cond) // wait signal
}
-
NSConditionLock
NSConditionLock *psy_conditionLock = [NSConditionLock new];
[psy_conditionLock lock];
[psy_conditionLock unlock];
源码:
@interface NSConditionLock : NSObject <NSLocking> {
@private
void *_priv;
}
- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;
@property (readonly) NSInteger condition;
- (void)lockWhenCondition:(NSInteger)condition;
- (BOOL)tryLock;
- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;
- (BOOL)lockBeforeDate:(NSDate *)limit;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;
@property (nullable, copy) NSString *name API_AVAILABLE(macos(10.5), ios(2.0), watchos(2.0), tvos(9.0));
@end
案例
NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[conditionLock lockWhenCondition:1];
printf("线程 1\n");
[conditionLock unlockWithCondition:0];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[conditionLock lockWhenCondition:2];
sleep(0.1);
printf("线程 2\n");
[conditionLock unlockWithCondition:1];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[conditionLock lock];
printf("线程 3\n");
[conditionLock unlock];
});
运行结果:
线程 3
线程 2
线程 1
别看NSConditionLock多了很多方法,其内部封装了NSCondition并在原来的基础上做一些扩展,其swift源码如下:
swift 源码
internal var _cond = NSCondition() // 直接使用NSCondition创建条件锁
internal var _value: Int // 定义一个条件值
internal var _thread: _swift_CFThreadRef?
public convenience override init() {
self.init(condition: 0) // init方法,condition默认为0
}
public init(condition: Int) {
_value = condition
}
open func lock() { // lock 默认 Date.distantFuture
let _ = lock(before: Date.distantFuture)
}
open func unlock() {
_cond.lock()
_thread = nil // 线程置空
_cond.broadcast() //
_cond.unlock()
}
open var condition: Int {
return _value
}
open func lock(whenCondition condition: Int) {
let _ = lock(whenCondition: condition, before: Date.distantFuture)
}
open func `try`() -> Bool {
return lock(before: Date.distantPast)
}
open func tryLock(whenCondition condition: Int) -> Bool {
return lock(whenCondition: condition, before: Date.distantPast)
}
open func unlock(withCondition condition: Int) {
_cond.lock()
_thread = nil
_value = condition
_cond.broadcast()
_cond.unlock()
}
open func lock(before limit: Date) -> Bool {
_cond.lock()
while _thread != nil {
if !_cond.wait(until: limit) {
_cond.unlock()
return false
}
}
_thread = pthread_self()
_cond.unlock()
return true
}
open func lock(whenCondition condition: Int, before limit: Date) -> Bool {
_cond.lock()
while _thread != nil || _value != condition {
if !_cond.wait(until: limit) {
_cond.unlock()
return false
}
}
_thread = pthread_self() //
_cond.unlock()
return true
}
-
@synchronized
其是一把互斥锁,但是前提是标识objc要一样,如果标识不一样,就达不到互斥效果。其具有可重入多线程可递归性,因为操作简单,功能强大,深受青睐。
这篇文章已经分析过@synchronized锁了
操作
@synchronized(objc) {
// 业务代码
}
案例
// @synchronized -> 多线程性+ 递归性
for (int i= 0; i<10; i++) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^testMethod)(int);
testMethod = ^(int value){
@synchronized (self) {
if (value > 0) {
printf("current value = %d\n",value);
testMethod(value - 1);
}
}
};
testMethod(10);
});
}
其输出结果是输出10次下列:
current value = 10
current value = 9
current value = 8
current value = 7
current value = 6
current value = 5
current value = 4
current value = 3
current value = 2
current value = 1
.................
网友评论