0%

iOS 线程安全-锁

Objective-C

使用 @synchronized 类实现锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 实例类 person
Person *person = [[Person alloc] init];
// 线程 A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(person) {
[person personA];
[NSThread sleepForTimeInterval:3];
// 线程休眠 3 秒
}
});
// 线程 B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
@synchronized(person) {
[person personB];
}
});

使用 NSRecursiveLock 类实现锁

递归锁,递归或循环方法时使用此方法实现锁,可避免死锁等问题

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
// 实例类 person
Person *person = [[Person alloc] init];

// 创建锁对象
NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];

// 创建递归方法
static void (^testCode)(int);
testCode = ^(int value) {
[theLock tryLock];
if (value > 0) {
[person personA];
[NSThread sleepForTimeInterval:1];
testCode(value - 1);
}
[theLock unlock];
};

//线程 A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
testCode(5);
});

//线程 B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[theLock lock];
[person personB];
[theLock unlock];
});

使用 NSConditionLock(条件锁)类实现锁

使用此方法可以指定,只有满足条件的时候才可以解锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 实例类 person
Person *person = [[Person alloc] init];
// 创建条件锁
NSConditionLock *conditionLock = [[NSConditionLock alloc] init];
// 线程 A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[conditionLock lock];
[person personA];
[NSThread sleepForTimeInterval:5];
[conditionLock unlockWithCondition:10];
});
// 线程 B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[conditionLock lockWhenCondition:10];
[person personB];
[conditionLock unlock];
});

NSDistributedLock(分布式锁)

在 iOS 中不需要用到,也没有这个方法,因此本文不作介绍,这里写出来只是想让大家知道有这个锁存在。
如果想要学习 NSDistributedLock 的话,你可以创建 MAC OS 的项目自己演练,方法请自行 Google,谢谢

C 语言

使用 pthread_mutex_t 实现锁

注意:必须在头文件导入:#import <pthread.h>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 实例类 person
Person *person = [[Person alloc] init];
// 创建锁对象
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);
// 线程 A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&mutex);
[person personA];
[NSThread sleepForTimeInterval:5];
pthread_mutex_unlock(&mutex);
});
// 线程 B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&mutex);
[person personB];
pthread_mutex_unlock(&mutex);
});

使用 GCD 实现“锁”(信号量)

GCD 提供一种信号的机制,使用它我们可以创建“锁”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 实例类 person
Person *person = [[Person alloc] init];

// 创建并设置信量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

// 线程 A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[person personA];
[NSThread sleepForTimeInterval:5];
dispatch_semaphore_signal(semaphore);
});

// 线程 B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[person personB];
dispatch_semaphore_signal(semaphore);
});

我在这里解释一下代码。 dispatch_semaphore_wait 方法是把信号量加 1, dispatch_semaphore_signal 是把信号量减 1。

我们把信号量当作是一个计数器,当计数器是一个非负整数时,所有通过它的线程都应该把这个整数减 1。

如果计数器大于 0,那么则允许访问,并把计数器减 1。如果为 0,则访问被禁止,所有通过它的线程都处于
等待的状态。

使用 POSIX(条件锁)创建锁)

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
// 实例类 person
Person *person = [[Person alloc] init];

// 创建互斥锁
__block pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);

// 创建条件锁
__block pthread_cond_t cond;
pthread_cond_init(&cond, NULL);

// 线程 A
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&mutex);
pthread_cond_wait(&cond, &mutex);
[person personA];
pthread_mutex_unlock(&mutex);
});

// 线程 B
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
pthread_mutex_lock(&mutex);
[person personB];
[NSThread sleepForTimeInterval:5];
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
});

效果:程序会首先调用线程 B,在 5 秒后再调用线程 A。因为在线程 A 中创建了等待条件锁,线程 B 有激活锁,只有当线程 B 执行完后会激活线程 A。

pthread_cond_wait 方法为等待条件锁。

pthread_cond_signal 方法为激活一个相同条件的条件锁。

注意:自旋锁 OSSpinLock 已经不再线程安全

参考资料