@synchronized补充答疑
@synchronized
底层原理主要是一个哈希结构,这里有几个疑问?
- 对于
lockCount
的理解 - 对于
threadCount
的理解 - 对于
整个拉链
的理解 - 对于
@synchronized
数据结构的理解 -
SyncList
对应的SyncData
是怎么创建的?或者说不同对象不同线程要如何处理?
SyncData
的创建
<!-- objc4-818.2源码 id2data方法 -->
posix_memalign((void **)&result, alignof(SyncData), sizeof(SyncData));
result->object = (objc_object *)object;
result->threadCount = 1;
new (&result->mutex) recursive_mutex_t(fork_unsafe_lock);
result->nextData = *listp;
*listp = result;
下面探索什么情况下才会创建SyncData
?
- 上节课我们打印
sDataList
发现有64个,下面修改源码减少个数进行调试,这样就增加了线程冲突的概率
<!-- 修改前 -->
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum { StripeCount = 8 };
#else
enum { StripeCount = 64 };
#endif
<!-- 修改后 -->
class StripedMap {
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
enum { StripeCount = 8 };
#else
enum { StripeCount = 1 };
#endif
-
main.m文件
添加代码进行调试


-
*listp = result;
对sDataList
进行赋值

- 下面两个地址不同,说明形成了
SyncData
的拉链,链表出现哈希冲突
调节出来


- 执行到
@synchronized (p2)
,当前p2
只被锁了一次

总结
-
哈希冲突
才会导致拉链的行程 - 每个
SyncData
里面都会有一个threadCount
,来标记被锁了多少次 -
lockCount
就是在同一线程下被锁的次数
锁的分类讲解
⾃旋锁
线程反复检查锁变量是否可用。由于线程在这一过程中保持执行,因此是一种忙等待。一旦获取了自旋锁,线程会一直保持该锁,直至显示释放自旋锁。自旋锁避免了进程上下文的调度开销,因此对于线程只会阻塞很短时间的场合是有效的。
OSSpinLock
os_unfair_lock
互斥锁
是一种用于多线程编程中,防止两条线程同时对同一公共资源(比如全局变量)进行读写的机制。该目的通过将代码切片成一个一个的临界区而达成。
NSLock
pthread_mutex
@synchronized
条件锁
就是条件变量,当进程的某些资源要求不满足时就进入休眠,也就是锁住了。当资源被分配到了,条件锁打开,进程继续运行。
NSCondition
NSConditionLock
递归锁
同一个线程可以加锁N次而不会引发死锁
NSRecursiveLock
pthread_mutex(recursive)
信号量(semaphore)
一种更高级的同步机制,互斥锁可以说是semaphore在仅取值0/1时的特例。信号量可以有更多的取值空间,用来实现更加复杂的同步,而不单单是线程间互斥。
dispatch_semaphore
读写锁
一种特殊的自旋锁。它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。
- 读写锁相对于自旋锁而言,能提高并发性。因为在多处理器系统中,它允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。但写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数先关),但不能同时既有读者又有写者。在读写锁保持期间也是抢占失效的;
- 如果读写锁当前没有读者,也没有写者,那么写者可以立刻获得读写锁,否则它必须自旋在那里,直到没有任何写者或读者;
- 如果读写锁没有写者,那么读者可以立即获得该读写锁,否则读者必须自旋在那里,直到写者释放该读写锁。
其实基本的锁就包括三类:⾃旋锁
、互斥锁
、读写锁
。其他的⽐如条件锁
、递归锁
、信号量
都是上层的封装和实现
-
互斥锁 = 互斥 + 同步
,互斥保证线程安全,当一条线程执行时,其他线程休眠。同步保证执行顺序,多线程串行执行
NSLock、pthread_mutex、@synchronized、NSCondition、NSConditionLock、NSRecursiveLock、pthread_mutex(recursive)、dispatch_semaphore
-
自旋锁 = 互斥 + 忙等
,例如do...while循环。它的优点在于不会引起调用者睡眠,所以不会进行线程调度,CPU时间片轮转等耗时操作。而缺点是当等待时会消耗大量CPU资源,所以自旋锁不适用较长时间的任务:
OSSpinLock、os_unfair_lock
NSLock和NSRecursiveLock的区别
案例一 NSLock
- (void)lg_testRecursive{
dispatch_async(dispatch_get_global_queue(0, 0), ^{
static void (^testMethod)(int);
testMethod = ^(int value){
if (value > 0) {
NSLog(@"current value = %d",value);
testMethod(value - 1);
}
};
testMethod(10);
});
}
// 控制台打印
2021-09-25 10:44:42.627284+0800 003-NSLock分析[21514:21767277] current value = 10
2021-09-25 10:44:42.627476+0800 003-NSLock分析[21514:21767277] current value = 9
2021-09-25 10:44:42.627603+0800 003-NSLock分析[21514:21767277] current value = 8
2021-09-25 10:44:42.627729+0800 003-NSLock分析[21514:21767277] current value = 7
2021-09-25 10:44:42.627847+0800 003-NSLock分析[21514:21767277] current value = 6
2021-09-25 10:44:42.627977+0800 003-NSLock分析[21514:21767277] current value = 5
2021-09-25 10:44:42.628104+0800 003-NSLock分析[21514:21767277] current value = 4
2021-09-25 10:44:42.628239+0800 003-NSLock分析[21514:21767277] current value = 3
2021-09-25 10:44:42.628383+0800 003-NSLock分析[21514:21767277] current value = 2
2021-09-25 10:44:42.628504+0800 003-NSLock分析[21514:21767277] current value = 1
如果对上面代码添加for循环
,修改代码如下
- (void)lg_testRecursive{
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) {
NSLog(@"current value = %d",value);
testMethod(value - 1);
}
};
testMethod(10);
});
}
}
// 控制台打印
2021-09-25 10:48:15.895123+0800 003-NSLock分析[21529:21769715] current value = 10
2021-09-25 10:48:15.895167+0800 003-NSLock分析[21529:21769720] current value = 10
2021-09-25 10:48:15.895196+0800 003-NSLock分析[21529:21769714] current value = 10
2021-09-25 10:48:15.895317+0800 003-NSLock分析[21529:21769715] current value = 9
2021-09-25 10:48:15.895323+0800 003-NSLock分析[21529:21769720] current value = 9
2021-09-25 10:48:15.895334+0800 003-NSLock分析[21529:21769714] current value = 9
2021-09-25 10:48:15.895460+0800 003-NSLock分析[21529:21769715] current value = 8
2021-09-25 10:48:15.895477+0800 003-NSLock分析[21529:21769720] current value = 8
2021-09-25 10:48:15.895537+0800 003-NSLock分析[21529:21769714] current value = 8
2021-09-25 10:48:15.895663+0800 003-NSLock分析[21529:21769715] current value = 7
......
通过上面打印发现数据错乱
,如果数据不断变化,来回切换产生递归,会引起多线程安全
问题。现在希望顺序打印,要怎么解决?如果添加锁,又该如何添加呢?下面进行尝试解决......
- (void)lg_testRecursive{
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){
[lock lock];
if (value > 0) {
NSLog(@"current value = %d",value);
testMethod(value - 1);
}
};
testMethod(10);
[lock unlock];
});
}
}
// 控制台打印
2021-09-25 10:59:53.087625+0800 003-NSLock分析[21623:21778908] current value = 10
产生死锁
执行完testMethod(10)
会进入block回调
,然后加锁,递归执行testMethod(value - 1);
无限加锁,产生死锁。需要把[lock lock];
加到testMethod回调block上面
才行,如下所示
- (void)lg_testRecursive{
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) {
NSLog(@"current value = %d",value);
testMethod(value - 1);
}
};
[lock lock];
testMethod(10);
[lock unlock];
});
}
}
案例二 NSRecursiveLock
无法多线程递归
- (void)lg_testRecursive{
self.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) {
NSLog(@"current value = %d",value);
testMethod(value - 1);
}
[self.recursiveLock unlock];
};
testMethod(10);
});
}
}
// 控制台打印
2021-09-25 11:13:54.098546+0800 003-NSLock分析[21778:21792829] current value = 10
2021-09-25 11:13:54.098705+0800 003-NSLock分析[21778:21792829] current value = 9
2021-09-25 11:13:54.098840+0800 003-NSLock分析[21778:21792829] current value = 8
2021-09-25 11:13:54.098972+0800 003-NSLock分析[21778:21792829] current value = 7
2021-09-25 11:13:54.099094+0800 003-NSLock分析[21778:21792829] current value = 6
2021-09-25 11:13:54.099214+0800 003-NSLock分析[21778:21792829] current value = 5
2021-09-25 11:13:54.099332+0800 003-NSLock分析[21778:21792829] current value = 4
2021-09-25 11:13:54.099455+0800 003-NSLock分析[21778:21792829] current value = 3
2021-09-25 11:13:54.099863+0800 003-NSLock分析[21778:21792829] current value = 2
2021-09-25 11:13:54.100512+0800 003-NSLock分析[21778:21792829] current value = 1
(lldb) 产生崩溃
NSRecursiveLock
递归锁应用在多线程中,无法实现多线程的可递归,从而引起崩溃。这就要使用到@synchronized
锁来解决多线程问题。
案例三 @synchronized(self)
- (void)lg_testRecursive{
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) {
NSLog(@"current value = %d",value);
testMethod(value - 1);
}
}
};
testMethod(10);
});
}
}
// 控制台打印
2021-09-25 11:19:59.301839+0800 003-NSLock分析[21809:21798006] current value = 10
2021-09-25 11:19:59.302010+0800 003-NSLock分析[21809:21798006] current value = 9
2021-09-25 11:19:59.302155+0800 003-NSLock分析[21809:21798006] current value = 8
2021-09-25 11:19:59.302294+0800 003-NSLock分析[21809:21798006] current value = 7
2021-09-25 11:19:59.302449+0800 003-NSLock分析[21809:21798006] current value = 6
2021-09-25 11:19:59.302591+0800 003-NSLock分析[21809:21798006] current value = 5
2021-09-25 11:19:59.302729+0800 003-NSLock分析[21809:21798006] current value = 4
2021-09-25 11:19:59.302899+0800 003-NSLock分析[21809:21798006] current value = 3
2021-09-25 11:19:59.303055+0800 003-NSLock分析[21809:21798006] current value = 2
2021-09-25 11:19:59.303185+0800 003-NSLock分析[21809:21798006] current value = 1
2021-09-25 11:19:59.303390+0800 003-NSLock分析[21809:21798007] current value = 10
......
NSRecursiveLock
锁解决了递归性,但是@synchronized
锁才会解决多线程行。
NSCondition的分析
NSCondition
对象实际上作为⼀个锁
和⼀个线程检查器
:锁主要
为了当检测条件时保护数据源
,执⾏条件引发的任务;线程检查器
主要是根据条件决定是否继续运⾏线程
,即线程是否被阻塞
-
[condition lock];
//⼀般⽤于多线程同时访问、修改同⼀个数据源,保证在同⼀时间内数据源只被访问、修改⼀次,其他线程的命令需要在lock 外等待,只到unlock ,才可访问 -
[condition unlock];
//与lock 同时使⽤ -
[condition wait];
//让当前线程处于等待状态 -
[condition signal];
//CPU发信号告诉线程不⽤在等待,可以继续执⾏
#pragma mark -- NSCondition
- (void)lg_testConditon{
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), ^{
[self lg_producer];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self lg_consumer];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self lg_consumer];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self lg_producer];
});
}
}
- (void)lg_producer{
self.ticketCount = self.ticketCount + 1;
NSLog(@"生产一个 现有 count %zd",self.ticketCount);
}
- (void)lg_consumer{
if (self.ticketCount == 0) {
NSLog(@"等待 count %zd",self.ticketCount);
}
self.ticketCount -= 1;
NSLog(@"消费一个 还剩 count %zd ",self.ticketCount);
}
// 控制台打印
2021-09-25 11:41:24.904076+0800 004-NSCondition[21959:21814557] 生产一个 现有 count 1
2021-09-25 11:41:24.905145+0800 004-NSCondition[21959:21814481] 生产一个 现有 count 1
2021-09-25 11:41:24.906589+0800 004-NSCondition[21959:21814518] 消费一个 还剩 count 1
2021-09-25 11:41:24.905742+0800 004-NSCondition[21959:21814559] 消费一个 还剩 count 0
2021-09-25 11:41:24.904336+0800 004-NSCondition[21959:21814547] 生产一个 现有 count 2
2021-09-25 11:41:24.905349+0800 004-NSCondition[21959:21814478] 生产一个 现有 count 2
2021-09-25 11:41:24.904900+0800 004-NSCondition[21959:21814519] 消费一个 还剩 count 0
2021-09-25 11:41:24.905543+0800 004-NSCondition[21959:21814482] 消费一个 还剩 count 1
2021-09-25 11:41:25.025766+0800 004-NSCondition[21959:21814563] 消费一个 还剩 count -1
......
通过上面打印我们发现,可消费的数量已经为0了,还在继续消费。这样就会产生错乱
,因为生产数量
与消费数量
要对应
,允许消费的数量大于0才能消费。解决方案是添加NSCondition
锁
#pragma mark -- NSCondition
- (void)lg_testConditon{
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), ^{
[self lg_producer];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self lg_consumer];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self lg_consumer];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self lg_producer];
});
}
}
- (void)lg_producer{
[_testCondition lock]; // 操作的多线程影响
self.ticketCount = self.ticketCount + 1;
NSLog(@"生产一个 现有 count %zd",self.ticketCount);
// 生产了一个要发送信号,让消费者进行消费
[_testCondition signal]; // 信号
[_testCondition unlock];
}
- (void)lg_consumer{
[_testCondition lock]; // 操作的多线程影响
// 消费为0的话要进行等待
if (self.ticketCount == 0) {
NSLog(@"等待 count %zd",self.ticketCount);
[_testCondition wait];
}
//注意消费行为,要在等待条件判断之后
self.ticketCount -= 1;
NSLog(@"消费一个 还剩 count %zd ",self.ticketCount);
[_testCondition unlock];
}
// 控制台打印
2021-09-25 11:46:57.842489+0800 004-NSCondition[21988:21818035] 生产一个 现有 count 1
2021-09-25 11:46:57.842663+0800 004-NSCondition[21988:21818037] 消费一个 还剩 count 0
2021-09-25 11:46:57.842831+0800 004-NSCondition[21988:21818038] 等待 count 0
2021-09-25 11:46:57.842973+0800 004-NSCondition[21988:21818034] 生产一个 现有 count 1
2021-09-25 11:46:57.843119+0800 004-NSCondition[21988:21818038] 消费一个 还剩 count 0
2021-09-25 11:46:57.843284+0800 004-NSCondition[21988:21818039] 等待 count 0
2021-09-25 11:46:57.843417+0800 004-NSCondition[21988:21818037] 等待 count 0
2021-09-25 11:46:57.843546+0800 004-NSCondition[21988:21818035] 生产一个 现有 count 1
2021-09-25 11:46:57.843882+0800 004-NSCondition[21988:21818039] 消费一个 还剩 count 0
2021-09-25 11:46:57.845138+0800 004-NSCondition[21988:21818039] 等待 count 0
......
foundation源码关于锁的封装
NSLock底层
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
@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
遵循NSLocking协议
,在swift的foundation源码
中可查看
public protocol NSLocking {
func lock()
func unlock()
}
open class NSLock: NSObject, NSLocking {
internal var mutex = _MutexPointer.allocate(capacity: 1)
#if os(macOS) || os(iOS) || os(Windows)
private var timeoutCond = _ConditionVariablePointer.allocate(capacity: 1)
private var timeoutMutex = _MutexPointer.allocate(capacity: 1)
#endif
public override init() {
#if os(Windows)
InitializeSRWLock(mutex)
InitializeConditionVariable(timeoutCond)
InitializeSRWLock(timeoutMutex)
#else
pthread_mutex_init(mutex, nil)
#if os(macOS) || os(iOS)
pthread_cond_init(timeoutCond, nil)
pthread_mutex_init(timeoutMutex, nil)
#endif
#endif
}
deinit {
#if os(Windows)
// SRWLocks do not need to be explicitly destroyed
#else
pthread_mutex_destroy(mutex)
#endif
mutex.deinitialize(count: 1)
mutex.deallocate()
#if os(macOS) || os(iOS) || os(Windows)
deallocateTimedLockData(cond: timeoutCond, mutex: timeoutMutex)
#endif
}
open func lock() {
#if os(Windows)
AcquireSRWLockExclusive(mutex)
#else
pthread_mutex_lock(mutex)
#endif
}
open func unlock() {
#if os(Windows)
ReleaseSRWLockExclusive(mutex)
AcquireSRWLockExclusive(timeoutMutex)
WakeAllConditionVariable(timeoutCond)
ReleaseSRWLockExclusive(timeoutMutex)
#else
pthread_mutex_unlock(mutex)
#if os(macOS) || os(iOS)
// Wakeup any threads waiting in lock(before:)
pthread_mutex_lock(timeoutMutex)
pthread_cond_broadcast(timeoutCond)
pthread_mutex_unlock(timeoutMutex)
#endif
#endif
}
底层是对pthread_mutex_init
、pthread_mutex_lock
、pthread_mutex_unlock
的封装
NSRecursiveLock底层
open class NSRecursiveLock: NSObject, NSLocking {
internal var mutex = _RecursiveMutexPointer.allocate(capacity: 1)
#if os(macOS) || os(iOS) || os(Windows)
private var timeoutCond = _ConditionVariablePointer.allocate(capacity: 1)
private var timeoutMutex = _MutexPointer.allocate(capacity: 1)
#endif
public override init() {
super.init()
#if os(Windows)
InitializeCriticalSection(mutex)
InitializeConditionVariable(timeoutCond)
InitializeSRWLock(timeoutMutex)
#else
#if CYGWIN
var attrib : pthread_mutexattr_t? = nil
#else
var attrib = pthread_mutexattr_t()
#endif
withUnsafeMutablePointer(to: &attrib) { attrs in
pthread_mutexattr_init(attrs)
// 递归设置
pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))
pthread_mutex_init(mutex, attrs)
}
#if os(macOS) || os(iOS)
pthread_cond_init(timeoutCond, nil)
pthread_mutex_init(timeoutMutex, nil)
#endif
#endif
}
deinit {
#if os(Windows)
DeleteCriticalSection(mutex)
#else
pthread_mutex_destroy(mutex)
#endif
mutex.deinitialize(count: 1)
mutex.deallocate()
#if os(macOS) || os(iOS) || os(Windows)
deallocateTimedLockData(cond: timeoutCond, mutex: timeoutMutex)
#endif
}
open func lock() {
#if os(Windows)
EnterCriticalSection(mutex)
#else
pthread_mutex_lock(mutex)
#endif
}
......
NSCondition底层
open class NSCondition: NSObject, NSLocking {
internal var mutex = _MutexPointer.allocate(capacity: 1)
internal var cond = _ConditionVariablePointer.allocate(capacity: 1)
public override init() {
#if os(Windows)
InitializeSRWLock(mutex)
InitializeConditionVariable(cond)
#else
pthread_mutex_init(mutex, nil)
pthread_cond_init(cond, nil)
#endif
}
deinit {
#if os(Windows)
// SRWLock do not need to be explicitly destroyed
#else
pthread_mutex_destroy(mutex)
pthread_cond_destroy(cond)
#endif
mutex.deinitialize(count: 1)
cond.deinitialize(count: 1)
mutex.deallocate()
cond.deallocate()
}
open func lock() {
#if os(Windows)
AcquireSRWLockExclusive(mutex)
#else
pthread_mutex_lock(mutex)
#endif
}
......
NSConditionLock分析上
请问下面代码执行顺序是什么?
- (void)lg_testConditonLock{
NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[conditionLock lockWhenCondition:1];
NSLog(@"线程 1");
[conditionLock unlockWithCondition:0];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
[conditionLock lockWhenCondition:2];
sleep(0.1);
NSLog(@"线程 2");
[conditionLock unlockWithCondition:1];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[conditionLock lock];
NSLog(@"线程 3");
[conditionLock unlock];
});
}
// 控制台打印
2021-09-25 15:21:33.617561+0800 004-NSCondition[23246:21892559] 线程 3
2021-09-25 15:21:33.621515+0800 004-NSCondition[23246:21892559] 线程 2
2021-09-25 15:21:33.624639+0800 004-NSCondition[23246:21892563] 线程 1
执行结果,大概率为线程3
、线程2
、线程1
。
NSConditionLock
初始化,设置的条件为2
。按照线程优先级顺序:
-
线程1优先级最高
,但不符合条件,代码块无法执行,进入等待状态
; -
线程3默认优先级
,同时属于无条件锁,可以执行代码块; -
线程2优先级最低
,但符合条件,也可以执行代码块;
所以,大概率线程3
会在线程2
之前打印,而线程1
必须等待线程2
执行完,释放条件锁之后才能执行。
这里我们有一些疑问?
-
NSConditionLock
与NSCondition
有关系吗? -
initWithCondition:2
中传的参数2
是做什么的? -
[conditionLock lockWhenCondition:1];
传的参数是如何控制的? -
unlockWithCondition
解锁方法做了什么?
基于上面提出的疑问?下面进行探索...
- 添加断点调试

- 添加符号断点
-[NSConditionLock initWithCondition:]

- 断点查看所有带
bl
跳转相关的汇编,进行打印调试

- 又调用一次
init
方法,这一次是NSConditionLock
调用了init
方法

- 调用
zone
方法,进行内存开辟

-
NSCondition
调用allocWithZone
方法

-
NSCondition
调用init
方法

上面对[NSConditionLock initWithCondition:]
的探索步骤如下
[? init:2]
-[NSConditionLock init]
-[NSConditionLock zone]
[NSCondition allocWithZone]
[NSCondition init]
NSConditionLock
中封装了一个NSCondition
,并且把传入的值为2的value
保存在NSCondition
中
NSConditionLock分析下
下面探索lockWhenCondition
与unlockWithCondition
底层原理
- 添加断点调试

- 添加符号断点
-[NSConditionLock lockWhenCondition:]
、-[NSConditionLock unlockWithCondition:]

-
NSDate
调用distantFuture
方法

-
-[NSConditionLock lockWhenCondition: beforeDate:]
方法调用,Condition
参数为传入的1,Date
参数传入的就是上一步的[NSDate distantFuture]

- 添加符号断点
-[NSConditionLock lockWhenCondition: beforeDate:]

-
NSCondition
调用lock
方法

- 调用
WaitUntilDate
方法

-
0x000000000000001
->返回true
不在等待

-
NSCondition
调用lock
方法

-
NSCondition
调用broadcast
方法

-
NSCondition
调用unlock
方法

上面对[NSConditionLock lockWhenCondition:]
的探索步骤如下
[NSDate distantFuture]
-[NSConditionLock lockWhenCondition: beforeDate:]
-[NSCondition lock]
[WaitUntilDate ]
-
0x000000000000001
->返回true
不在等待 [NSCondition unlock]
[NSConditionLock unlockWhenCondition:]
的探索步骤如下
[NSCondition lock]
[NSCondition broadcast]
[NSCondition unlock]
查看源码流程与上面探索流程对比
open class NSConditionLock : NSObject, NSLocking {
internal var _cond = NSCondition()
internal var _value: Int
internal var _thread: _swift_CFThreadRef?
public convenience override init() {
self.init(condition: 0)
}
public init(condition: Int) {
_value = condition
}
open func lock() {
let _ = lock(before: Date.distantFuture)
}
open func unlock() {
_cond.lock()
#if os(Windows)
_thread = INVALID_HANDLE_VALUE
#else
_thread = nil
#endif
_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()
#if os(Windows)
_thread = INVALID_HANDLE_VALUE
#else
_thread = nil
#endif
_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
}
}
#if os(Windows)
_thread = GetCurrentThread()
#else
_thread = pthread_self()
#endif
_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
}
}
#if os(Windows)
_thread = GetCurrentThread()
#else
_thread = pthread_self()
#endif
_cond.unlock()
return true
}
open var name: String?
}
NSConditionLock总结
- 线程 1 调用
[NSConditionLock lockWhenCondition:]
,此时此刻因为不满足当前条件,所以会进入waiting
状态,当前进入到waiting
时,会释放当前的互斥锁。 - 此时当前的线程 3 调用
[NSConditionLock lock:]
,本质上是调用[NSConditionLock lockBeforeDate:]
,这里不需要比对条件值,所以线程 3 会打印 - 接下来线程 2 执行
[NSConditionLock lockWhenCondition:]
,因为满足条件值,所以线程2
会打印,打印完成后会调用[NSConditionLock unlockWithCondition:]
,这个时候将value
设置为 1,并发送boradcast
, 此时线程 1 接收到当前的信号,唤醒执行并打印。 - 自此当前打印为
线程 3
->线程 2
->线程 1
。 -
[NSConditionLock lockWhenCondition:]:
这里会根据传入的condition 值和 Value 值进行对比,如果不相等,这里就会阻塞,进入线程池,否则的话就继续代码执行 -
[NSConditionLock unlockWithCondition:]:
这里会先更改当前的 value值,然后进行广播,唤醒当前的线程。
网友评论