多线程的安全隐患

  • 资源共享
    • 1块资源可能会被多个线程共享,也就是多个线程可能会访问同一块资源
    • 比如多个线程访问同一个对象、同一个变量、同一个文件
  • 当多个线程访问同一块资源时,很容易引发数据错乱和数据安全问题

多线程的安全隐患的解决方案

  • 解决方案:使用线程同步技术(同步,就是协同步调,按预定的先后次序进行)
  • 常见的线程同步技术是:加锁

iOS中的线程同步方案

  • OSSpinLock
  • os_unfair_lock
  • pthread_mutex
  • dispatch_semaphore
  • dispatch_queue(DISPATCH_QUEUE_SERIAL)
  • NSLock
  • NSRecursiveLock
  • NSCondition
  • NSConditionLock
  • @synchronized

OSSpinLock

  • OSSpinLock叫做”自旋锁”,等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源
  • 目前已经不再安全,可能会出现优先级反转问题
    • 如果等待锁的线程优先级较高,它会一直占用着CPU资源,优先级低的线程就无法释放锁
    • 需要导入头文件#import <libkern/OSAtomic.h>

用法:

1
2
3
4
5
6
7
8
//初始化
OSSpinLock lock = OS_SPINLOCK_INIT;
//尝试加锁(如果需要等待就不加锁,直接返回false;如果不需要等待就加锁,返回true)
bool result = OSSpinLockTry(&lock);
//加锁
OSSpinLockLock(&lock);
//解锁
OSSpineLockUnlock(&lock);

示例:

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
#import "OSSpinLockDemo.h"
#import <libkern/OSAtomic.h>

@interface OSSpinLockDemo()
// High-level lock
@property (assign, nonatomic) OSSpinLock moneyLock;
@property (assign, nonatomic) OSSpinLock ticketLock;
@end

@implementation OSSpinLockDemo

- (instancetype)init {
if (self = [super init]) {
self.moneyLock = OS_SPINLOCK_INIT;
self.ticketLock = OS_SPINLOCK_INIT;
}
return self;
}
- (void)__drawMoney {
OSSpinLockLock(&_moneyLock);
[super __drawMoney];
OSSpinLockUnlock(&_moneyLock);
}
- (void)__saveMoney {
OSSpinLockLock(&_moneyLock);
[super __saveMoney];
OSSpinLockUnlock(&_moneyLock);
}
- (void)__saleTicket {
OSSpinLockLock(&_ticketLock);
[super __saleTicket];
OSSpinLockUnlock(&_ticketLock);
}
@end

os_unfair_lock

  • os_unfair_lock用于取代不安全的OSSpinLock ,从iOS10开始才支持
  • 从底层调用看,等待os_unfair_lock锁的线程会处于休眠状态,并非忙等
  • 需要导入头文件#import <os/lock.h>

用法

1
2
3
4
5
6
7
8
//初始化
os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
//尝试加锁
os_unfair_lock_trylock(&lock);
//加锁
os_unfair_lock_lock(&lock);
// 解锁
os_unfair_lock_unlock(&lock);

示例:

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
#import "OSUnfairLockDemo.h"
#import <os/lock.h>

@interface OSUnfairLockDemo()
// Low-level lock
// ll lock
// lll
// Low-level lock的特点等不到锁就休眠
@property (assign, nonatomic) os_unfair_lock moneyLock;
@property (assign, nonatomic) os_unfair_lock ticketLock;
@end

@implementation OSUnfairLockDemo

- (instancetype)init {
if (self = [super init]) {
self.moneyLock = OS_UNFAIR_LOCK_INIT;
self.ticketLock = OS_UNFAIR_LOCK_INIT;
}
return self;
}

// 死锁:永远拿不到锁
- (void)__saleTicket {
os_unfair_lock_lock(&_ticketLock);
[super __saleTicket];
os_unfair_lock_unlock(&_ticketLock);
}
- (void)__saveMoney {
os_unfair_lock_lock(&_moneyLock);
[super __saveMoney];
os_unfair_lock_unlock(&_moneyLock);
}
- (void)__drawMoney {
os_unfair_lock_lock(&_moneyLock);
[super __drawMoney];
os_unfair_lock_unlock(&_moneyLock);
}
@end

pthread_mutex

  • mutex叫做”互斥锁”,等待锁的线程会处于休眠状态
  • 需要导入头文件#import <pthread.h>

用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 初始化锁属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// 初始化锁
pthread_mutex_t mutex;
pthread_mutex_init(&mutex, &attr);
// 尝试加锁
pthread_mutex_trylock(&mutex);
// 加锁
pthread_mutex_lock(&mutex);
// 解锁
pthread_mutex_unlock(&mutex);
// 销毁相关资源
pthread_mutexattr_destroy(&attr); pthread_mutex_destroy(&mutex);

示例:

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
#import "MutexDemo.h"
#import <pthread.h>

@interface MutexDemo()
@property (assign, nonatomic) pthread_mutex_t ticketMutex;
@property (assign, nonatomic) pthread_mutex_t moneyMutex;
@end

@implementation MutexDemo

- (void)__initMutex:(pthread_mutex_t *)mutex {
// 静态初始化
// pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// // 初始化属性
// pthread_mutexattr_t attr;
// pthread_mutexattr_init(&attr);
// pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
// // 初始化锁
// pthread_mutex_init(mutex, &attr);
// // 销毁属性
// pthread_mutexattr_destroy(&attr);

// 初始化属性
// pthread_mutexattr_t attr;
// pthread_mutexattr_init(&attr);
// pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT)
// //初始化锁
// pthread_mutex_init(mutex, attr);
// // 销毁属性
// pthread_mutexattr_destroy(&attr);

// 初始化锁,第二个参数传NULL时,属性可以不用设置
pthread_mutex_init(mutex, NULL);
}

- (instancetype)init {
if (self = [super init]) {
[self __initMutex:&_ticketMutex];
[self __initMutex:&_moneyMutex];
}
return self;
}

// 死锁:永远拿不到锁
- (void)__saleTicket {
pthread_mutex_lock(&_ticketMutex);
[super __saleTicket];
pthread_mutex_unlock(&_ticketMutex);
}
- (void)__saveMoney {
pthread_mutex_lock(&_moneyMutex);
[super __saveMoney];
pthread_mutex_unlock(&_moneyMutex);
}
- (void)__drawMoney {
pthread_mutex_lock(&_moneyMutex);
[super __drawMoney];
pthread_mutex_unlock(&_moneyMutex);
}
- (void)dealloc {
pthread_mutex_destroy(&_moneyMutex);
pthread_mutex_destroy(&_ticketMutex);
}
@end

pthread_mutex – 递归锁

1
2
3
4
5
6
7
/*
* Mutex type attributes
*/
#define PTHREAD_MUTEX_NORMAL 0
#define PTHREAD_MUTEX_ERRORCHECK 1
#define PTHREAD_MUTEX_RECURSIVE 2 //递归属性
#define PTHREAD_MUTEX_DEFAULT PTHREAD_MUTEX_NORMAL

示例:

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
#import "MutexDemo2.h"
#import <pthread.h>

@interface MutexDemo2()
@property (assign, nonatomic) pthread_mutex_t mutex;
@end

@implementation MutexDemo2

- (void)__initMutex:(pthread_mutex_t *)mutex {
// 递归锁:允许同一个线程对一把锁进行重复加锁

// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 初始化锁
pthread_mutex_init(mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);
}

- (instancetype)init {
if (self = [super init]) {
[self __initMutex:&_mutex];
}
return self;
}

/**
线程1:otherTest(+-)
otherTest(+-)
otherTest(+-)

线程2:otherTest(等待)
*/
- (void)otherTest {
pthread_mutex_lock(&_mutex);
NSLog(@"%s", __func__);
static int count = 0;
if (count < 10) {
count++;
[self otherTest];
//[self otherTest];
}
pthread_mutex_unlock(&_mutex);
}

//- (void)otherTest2
//{
// pthread_mutex_lock(&_mutex2);
// NSLog(@"%s", __func__);
// pthread_mutex_unlock(&_mutex2);
//}

- (void)dealloc {
pthread_mutex_destroy(&_mutex);
}
@end

pthread_mutex – 条件

用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 初始化锁
pthread_mutex_t mutex;
// 初始化锁属性,NULL代表使用默认属性
pthread_mutex_init(&mutex, NULL);
// 初始化条件
pthread_cond_t condition;
pthread_cont_init(&condition, NULL);
// 等待条件(进入休眠,放开mutext锁;被唤醒后,会再次对mutex加锁)
pthread_cond_wait(&condition, &mutex);
// 激活一个等待该条件的线程
pthread_cond_signal(&condition);
// 激活所有等待该条件的线程
pthread_cond_broadcast(&condition);
// 销毁相关资源
pthread_mutexattr_destroy(&attr); pthread_mutex_destroy(&mutex);

示例:

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
#import "MutexDemo3.h"
#import <pthread.h>

@interface MutexDemo3()
@property (assign, nonatomic) pthread_mutex_t mutex;
@property (assign, nonatomic) pthread_cond_t cond;
@property (strong, nonatomic) NSMutableArray *data;
@end

@implementation MutexDemo3

- (instancetype)init {
if (self = [super init]) {
// 初始化属性
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
// 初始化锁
pthread_mutex_init(&_mutex, &attr);
// 销毁属性
pthread_mutexattr_destroy(&attr);

// 初始化条件
pthread_cond_init(&_cond, NULL);

self.data = [NSMutableArray array];
}
return self;
}

- (void)otherTest {
[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];

[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];
}

// 生产者-消费者模式

// 线程1
// 删除数组中的元素
- (void)__remove {
pthread_mutex_lock(&_mutex);
NSLog(@"__remove - begin");

if (self.data.count == 0) {
// 等待
pthread_cond_wait(&_cond, &_mutex);
}

[self.data removeLastObject];
NSLog(@"删除了元素");

pthread_mutex_unlock(&_mutex);
}

// 线程2
// 往数组中添加元素
- (void)__add {
pthread_mutex_lock(&_mutex);

sleep(1);

[self.data addObject:@"Test"];
NSLog(@"添加了元素");

// 信号
pthread_cond_signal(&_cond);
// 广播
// pthread_cond_broadcast(&_cond);
pthread_mutex_unlock(&_mutex);
}

- (void)dealloc{
pthread_mutex_destroy(&_mutex);
pthread_cond_destroy(&_cond);
}
@end

NSLock、NSRecursiveLock

  • NSLock是对mutex普通锁的封装
  • NSRecursiveLock也是对mutex递归锁的封装,API跟NSLock基本一致

用法

1
2
3
4
5
6
7
8
9
10
11
12
13
@interface NSLock: NSObject<NSLocking> {
- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;
}
@end

@protocol NSLocking
- (void)lock;
- (void)unlock;
@end

// 初始化锁
NSLock *lock = [[NSLock alloc] init];

示例:

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
#import "NSLockDemo.h"

@interface NSLockDemo()
@property (strong, nonatomic) NSLock *ticketLock;
@property (strong, nonatomic) NSLock *moneyLock;
@end

@implementation NSLockDemo

- (instancetype)init {
if (self = [super init]) {
self.ticketLock = [[NSLock alloc] init];
self.moneyLock = [[NSLock alloc] init];
}
return self;
}

// 死锁:永远拿不到锁
- (void)__saleTicket {
[self.ticketLock lock];
[super __saleTicket];
[self.ticketLock unlock];
}

- (void)__saveMoney {
[self.moneyLock lock];
[super __saveMoney];
[self.moneyLock unlock];
}

- (void)__drawMoney {
[self.moneyLock lock];
[super __drawMoney];
[self.moneyLock unlock];
}
@end

参考