一、互斥锁
互斥锁 = 互斥 + 同步
闲等
1.1递归锁
NSRecursiveLock
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);
});
}
上面的代码打印发现,打印结果是有序的,但是本来应该打印10遍的,结果只打印了1遍。
该锁是递归锁
,但是不支持多线程的递归
。
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) {
NSLog(@"current value = %d",value);
testMethod(value - 1);
}
}
};
testMethod(10);
});
}
上面的打印结果是正确的,Synchronized
锁,是多线程递归锁。
1.2非递归锁
NSLock
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];
});
}
NSLock底层原理分析:
跟踪 [lock lock];
@protocol NSLocking
- (void)lock;
- (void)unlock;
@end
@interface NSLock : NSObject <NSLocking> {
发现 lock
其实是NSLocking
协议的一个方法,那么我们进一步分析源码 swift-corelibs-foundation-master
首先我们发现底层加锁的代码都是一样的如下所示:
pthread_mutex_lock(mutex)
pthread_mutex_unlock(mutex)
不一样的地方在于,递归锁NSRecursiveLock
多了如下的代码设置:
withUnsafeMutablePointer(to: &attrib) { attrs in
pthread_mutexattr_init(attrs)
pthread_mutexattr_settype(attrs, Int32(PTHREAD_MUTEX_RECURSIVE))//关键
pthread_mutex_init(mutex, attrs)
}
条件锁加了如下的处理:
cond.deinitialize(count: 1)
pthread_cond_wait(cond, mutex)
pthread_cond_signal(cond)
上面的代码只打印了一次,然后就卡主了。
NSLock
是非递归锁,因此上面的代码其实是多次在加锁,但是无法解锁。
二、自旋锁
自旋锁 = 互斥 + 忙等
NSCondication 条件锁
生产者-消费者模型
- (void)lg_testConditon{
_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]; // 操作的多线程影响
if (self.ticketCount == 0) {
NSLog(@"等待 count %zd",self.ticketCount);
[_testCondition wait];
}
//注意消费行为,要在等待条件判断之后
self.ticketCount -= 1;
NSLog(@"消费一个 还剩 count %zd ",self.ticketCount);
[_testCondition unlock];
}
NSConditionLock
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];
});
执行结果: 3 2 1
NSConditionLock底层原理
汇编跟踪分析
下符号断点,由于 initWithCondition
是个类对象方法,因此符号为 [NSConditionLock initWithCondition:]
(lldb) register read x0
x0 = 0x00000002817bc630
(lldb) register read x1
x1 = 0x00000001deda54ab
(lldb) register read x2
x2 = 0x0000000000000002
(lldb) po 0x00000002817bc630
<NSConditionLock: 0x2817bc630>{condition = 0, name = nil}
(lldb) po (SEL)0x00000001deda54ab
"initWithCondition:"
(lldb) po 0x0000000000000002
2
因为 bl
有相关的跳转处理,因此跟踪bl
:
1、找到第一个bl
的地方:
(lldb) register read x0
x0 = 0x000000016d4798b8
(lldb) register read x1
x1 = 0x00000001df7fd10c
(lldb) register read x2
x2 = 0x0000000000000002
(lldb) po 0x000000016d4798b8
6128375992
(lldb) po (SEL)0x00000001df7fd10c
"init"
可以知道,这里有一个对象调用了 init
方法,参数为2
.
2、找到第二个bl
的地方:
(lldb) register read x0
x0 = 0x00000002838102a0
(lldb) register read x1
x1 = 0x00000001df7fd10c
(lldb) register read x2
x2 = 0x0000000000000002
(lldb) po 0x00000002838102a0
<NSConditionLock: 0x2838102a0>{condition = 0, name = nil}
(lldb) po 0x00000001df7fd10c
8044663052
(lldb) po (SEL)0x00000001df7fd10c
"init"
可以知道,这里NSConditionLock
调用了 init
方法,参数为2
.
3、找到第三个bl
的地方:
(lldb) register read x0
x0 = 0x00000002838102a0
(lldb) register read x1
x1 = 0x00000001deb4f88c
(lldb) register read x2
x2 = 0x0000000000000002
(lldb) po 0x00000002838102a0
<NSConditionLock: 0x2838102a0>{condition = 0, name = nil}
(lldb) po (SEL)0x00000001deb4f88c
"zone"
可以知道,这里NSConditionLock
调用了 zone
方法,参数为2
.
4、找到第四个bl
的地方:
(lldb) register read x0
x0 = 0x00000001ee2da7a8 (void *)0x00000001ee2da7d0: NSCondition
(lldb) register read x1
x1 = 0x00000001de9bad0c
(lldb) register read x2
x2 = 0x00000001eeafc000 libsystem_malloc.dylib`virtual_default_zone
(lldb) po 0x00000001ee2da7a8
NSCondition
(lldb) po (SEL)0x00000001de9bad0c
"allocWithZone:"
(lldb) po 0x00000001eeafc000
8299462656
可以知道,这里NSCondition
调用了 allocWithZone
方法.
5、找到第五个bl
的地方:
(lldb) register read x0
x0 = 0x00000002821a4000
(lldb) register read x1
x1 = 0x00000001df7fd10c
(lldb) register read x2
x2 = 0xffffffffffffffd0
(lldb) po 0x00000002821a4000
<NSCondition: 0x2821a4000>{name = nil}
(lldb) po (SEL)0x00000001df7fd10c
"init"
(lldb) po 0xffffffffffffffd0
²O
(lldb)
可以知道,这里NSCondition
调用了 init
方法.
6、我们来到retab
这里
(lldb) register read x0
x0 = 0x0000000281df0030
(lldb) po 0x0000000281df0030
<NSConditionLock: 0x281df0030>{condition = 2, name = nil}
(lldb) x/6gx 0x0000000281df0030
0x281df0030: 0x01000001ee2da849 0x0000000000000000
0x281df0040: 0x00000002821a4000 0x0000000000000000
0x281df0050: 0x0000000000000002 0x0000000000000000
(lldb) po 0x00000002821a4000
<NSCondition: 0x2821a4000>{name = nil}
可以得出, NSConditionLock
里面有一个NSCondition
成员变量。
7、继续下符号断点:[NSConditionLock lockWhenCondition:1]
和 [NSConditionLock unlockWithCondition:0]
发现调用了distantFuture
。
8、
9、继续下符号断点[NSConditionLock lockWhenCondition:beforeDate:]
10、
11、
12、
看到[NSCondition lock]
看到[NSCondition broadcast]
14、
看到[NSCondition unlock]
小结:
- [NSConditionLock initWithCondition:]
1: [? init:2]
2: [NSConditionLock init]
3: [NSConditionLock zone:]
4: [NSCondition allocWithZone]
5: [NSCondition init]
- [NSConditionLock lockWhenCondition:1]
1: [NSDate distantFuture]
2: [NSConditionLock lockWhenCondition:beforeDate:]
2.1: [NSCondition lock];
2.2: waitUntilDate
2.3: 0x1 1 ->: 不再等待
2.4: unlock
- SConditionLock unlockWithCondition:0]
1: [Condition lock]
2: [Condition broadcast]
3: [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?
}
三、读写锁
读写锁的实现:
/**
读写锁:
1、多读单写
2、写写互斥
3、读写互斥
采用的方法:
1、写操作采用函数:dispatch_barrier_async,保证了写写互斥,读写互斥,不阻塞主线程任务的执行
2、读操作: 并发队列 ,dispatch_sync (之所以不使用dispatch_async,是为了防止返回值为空的情况出现) ,保证了多读
*/
@property (nonatomic, strong) NSMutableDictionary *dict;
@property (nonatomic, strong) dispatch_queue_t queue;
self.dict = [NSMutableDictionary dictionary];
self.queue = dispatch_queue_create("bingfaduilie", DISPATCH_QUEUE_CONCURRENT);
[self writeDataWithName:@"我的"];
[self writeDataWithName:@"名字是"];
[self writeDataWithName:@"jim"];
#pragma mark -- 写
- (void)writeDataWithName:(NSString *)name{
dispatch_barrier_async(self.queue, ^{
[self.dict setValue:name forKey:@"name"];
NSLog(@"%@",name);
});
}
#pragma mark -- 读
- (NSString *)readData{
__block NSString *name = @"";
dispatch_sync(self.queue, ^{
name = self.dict[@"name"];
});
return name;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
dispatch_async(self.queue, ^{
NSLog(@"%@", [self readData]);
});
}