iOS底层原理 多线程之安全锁以及常用的读写锁 --(11)

只要提到了多线程就应该想到线程安全,那么怎么做才能做到在多个线程中保证安全呢?
这篇文章主要讲解线程安全。

线程安全

线程安全是什么呢?摘抄一段百度百科的一段话

线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

为什么需要线程安全

ATM肯定用过,你要是边取钱,边存钱,会出问题吗?当你取钱的时候,正在取,结果有人汇款正好到账,本来1000块取了100剩下900,结果到账200,1000+200=1200,因为你取的时候,还没取完,汇款到账了结果数字又加上去了。你取的钱跑哪里去了,这里就需要取钱的时候不能写入数据,就是汇款需要在你取钱完成之后再汇款,不能同时进行。

那么在iOS中,锁是如何使用的呢?

自旋锁 OS_SPINLOCK

什么是优先级反转

简单从字面上来说,就是低优先级的任务先于高优先级的任务执行了,优先级搞反了。那在什么情况下会生这种情况呢?

假设三个任务准备执行,A,B,C,优先级依次是A>B>C;

首先:C处于运行状态,获得CPU正在执行,同时占有了某种资源;

其次:A进入就绪状态,因为优先级比C高,所以获得CPU,A转为运行状态;C进入就绪状态;

第三:执行过程中需要使用资源,而这个资源又被等待中的C占有的,于是A进入阻塞状态,C回到运行状态;

第四:此时B进入就绪状态,因为优先级比C高,B获得CPU,进入运行状态;C又回到就绪状态;

第五:如果这时又出现B2,B3等任务,他们的优先级比C高,但比A低,那么就会出现高优先级任务的A不能执行,反而低优先级的B,B2,B3等任务可以执行的奇怪现象,而这就是优先反转。

OS_SPINLOCK叫做自旋锁,等待锁的进程会处于忙等(busy-wait)状态,一直占用着CPU资源,目前已经不安全,可能会出现优先级翻转问题。

OS_SPINLOCKAPI

1
2
3
4
5
6
7
8
9
10
//初始化 一般是0,或者直接数字0也是ok的。
#define OS_SPINLOCK_INIT 0
//锁的初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
//尝试加锁
bool ret = OSSpinLockTry(&lock);
//加锁
OSSpinLockLock(&lock);
//解锁
OSSpinLockUnlock(&lock);

OSSpinLock简单实现12306如何卖票

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
//基类实现的卖票
- (void)__saleTicket{
NSInteger oldCount = self.ticketsCount;
if (isLog) {
sleep(sleepTime);
}
oldCount --;
self.ticketsCount = oldCount;
if (isLog) {
printf("还剩% 2ld 张票 - %s \n",(long)oldCount,[NSThread currentThread].description.UTF8String);
}

}



- (void)ticketTest{
self.ticketsCount = 10000;
NSInteger count = self.ticketsCount/3;
dispatch_queue_t queue = dispatch_queue_create("tick.com", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
if (time1 == 0) {
time1 = CFAbsoluteTimeGetCurrent();
}
for (int i = 0; i < count; i ++) {
[self __saleTicket];
}
});

dispatch_async(queue, ^{
if (time1 == 0) {
time1 = CFAbsoluteTimeGetCurrent();
}
for (int i = 0; i < count; i ++) {
[self __saleTicket];
}
});
dispatch_async(queue, ^{
if (time1 == 0) {
time1 = CFAbsoluteTimeGetCurrent();
}
for (int i = 0; i < count; i ++) {
[self __saleTicket];
}
});
dispatch_barrier_async(queue, ^{
CFAbsoluteTime time = CFAbsoluteTimeGetCurrent() - time1;
printf("tick cost time:%f",time);
});
}
- (void)__getMonery{
OSSpinLockLock(&_moneyLock);
[super __getMonery];
OSSpinLockUnlock(&_moneyLock);
}
- (void)__saleTicket{
OSSpinLockLock(&_moneyLock);
[super __saleTicket];
OSSpinLockUnlock(&_moneyLock);
}
- (void)__saveMonery{
OSSpinLockLock(&_moneyLock);
[super __saveMonery];
OSSpinLockUnlock(&_moneyLock);
}

- (void)__saleTicket{
NSInteger oldCount = self.ticketsCount;
oldCount --;
self.ticketsCount = oldCount;
}
//log
还剩 9 张票 - <NSThread: 0x600003dc6080>{number = 3, name = (null)}
还剩 8 张票 - <NSThread: 0x600003dc6080>{number = 3, name = (null)}
还剩 7 张票 - <NSThread: 0x600003dc6080>{number = 3, name = (null)}
还剩 6 张票 - <NSThread: 0x600003df3a00>{number = 4, name = (null)}
还剩 5 张票 - <NSThread: 0x600003df3a00>{number = 4, name = (null)}
还剩 4 张票 - <NSThread: 0x600003df3a00>{number = 4, name = (null)}
还剩 3 张票 - <NSThread: 0x600003dc0000>{number = 5, name = (null)}
还剩 2 张票 - <NSThread: 0x600003dc0000>{number = 5, name = (null)}
还剩 1 张票 - <NSThread: 0x600003dc0000>{number = 5, name = (null)}

汇编分析

1
2
3
4
5
6
7
8
9
10
for (NSInteger i = 0; i < 5; i ++) {
[[[NSThread alloc]initWithTarget:self selector:@selector(__saleTicket) object:nil] start];
}