谈到属性的原子性,即操作读写的原子性(安全的,单一的,完整的) 可以这么理解。那有什么方法可以做到?自然,我们想到了锁的概念。
锁的种类
我们在开发Apps的时候,经常会用到多线程。而锁的概念可以抽象成线程同步。主要作用即保护授权某段代码块的原子性执行。iOS中锁有很多种类:
- 信号量 Semaphore (允许N个有限线程在某一时间执行指定代码块)
- 线程锁 Mutex 保证在某一时间内执行某代码块只有一个线程进入。
- 自璇锁 Spinlock 指的是一个线程获取了一个自旋锁后,另外一个线程期望获取该自旋锁,获取不到的情况下会循环等待,不断的判断。
- 读写锁 Read-write-lock 并发只读,串行写。
- 递归锁 Recursive-lock 这个锁可以被同一线程多次请求,而不会引起死锁。它主要是用在循环或递归操作中。
Lock
NSLock 和 NSRecursiveLock 都是OC类,在Swift中直接使用。 C语言线程锁pthread_mutex_t可以在Swift中直接使用。Spinlock
自璇锁在iOS10被抛弃了,在Swift中没有与之对应的锁。相近的有os_unfair_lock(不会在争夺锁时候循环判断,而是在操作系统内核中等待被解锁唤醒)Read-write lock
读写锁在Swift中可以用pthread_rwlock_tSemaphore
在Swift中,通过DispatchSemaphore来实现信号数控制。
锁举例Demo
所有锁都有加锁解锁行为,我们给它定义这样一个协议动作。1
2
3
4protocol Lock {
func lock()
func unlock()
}
Mutex & SpinLock
那么,我们把pthread_mutex_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
31extension NSLock: Lock {}
//拟自旋锁
final class SpinLock: Lock {
private var unfairLock = os_unfair_lock_s()
func lock() {
os_unfair_lock_lock(&unfairLock)
}
func unlock() {
os_unfair_lock_unlock(&unfairLock)
}
}
//线程锁
final class Mutex: Lock {
private var mutex: pthread_mutex_t = {
var mutex = pthread_mutex_t()
pthread_mutex_init(&mutex, nil)
return mutex
}()
func lock() {
pthread_mutex_lock(&mutex)
}
func unlock() {
pthread_mutex_unlock(&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
26struct AtomicProperty {
private var underlyingFoo = 0
private let lock: Lock
init(lock: Lock) {
self.lock = lock
}
var foo: Int {
get {
lock.lock()
let value = underlyingFoo
lock.unlock()
return value
}
set {
lock.lock()
underlyingFoo = newValue
lock.unlock()
}
}
}
// Usage
let sample = AtomicProperty(lock: SpinLock())
_ = sample.foo
当然为了达到更好的性能,读取属性我们提供并发读取。可以用读写锁来实现。
ReadWriteLock
1 | final class ReadWriteLock { |
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//递归锁
let lock = NSRecursiveLock() //如果用NSLock() 将发生死锁,递归第二次进入再次lock()
var recursiveMethod: ((Int) -> Void)! = nil
recursiveMethod = { value in
defer {
lock.unlock()
}
lock.lock()
guard value > 0 else {
return
}
print(value)
sleep(2)
recursiveMethod(value - 1)
}
DispatchQueue.global().async {
print("开始")
recursiveMethod(5)
print("结束")
}
DispatchSemaphore
通过信号量来控制线程数1
2
3
4
5
6
7
8
9
10
11
12let concurrentTasks = 1
let queue = DispatchQueue(label: "Concurrent queue", attributes: .concurrent)
let sema = DispatchSemaphore(value: concurrentTasks)
for _ in 0..<100 {
queue.async {
// Do work
sema.signal()
}
sema.wait()
}
在另外一篇文章中,创建一个线程安全的字典中讲到,可以用栅栏函数dispatch_barrier_async来将操作任务串行化执行保证原子性。或者直接用NSLock也行,依据业务情况,使用对应符合场景的锁。