前语
在 iOS 中,我们能够运用 Barrier 来对并发行列中的一个或者多个使命进行同步调度。也便是说,它答应你将线程不安全的目标转换为线程安全的目标。它为在并发调度行列中履行的代码块创立一个同步点。
派发 Barrier 使命是在处理并发行列时充任串行行列款式目标的一组函数。运用 GCD的 Barrier API 能够保证提交的使命是指定行列在特定时间内唯一履行的使命。
这意味着在调度 Barrier 使命之前提交到行列的一切使命必须在 Barrier 使命履行之前完成。当轮到 Barrier 使命时,GCD 保证行列在此期间不履行任何其他使命。当 Barrier 使命完成后,行列会回来到其默认完成。GCD 供给同步和异步 Barrier 函数。
代码示例
假定现在你有三项使命,需求是使命一和三的代码能够并发履行,使命二的项目需求串行履行,且使命二需求在使命一履行完再履行,使命三需求使命二履行完再履行。示例代码如下:
let concurrentQueue = DispatchQueue(label: "com.demo.dispatchBarrier", attributes: .concurrent)
for i in 1...5 {
concurrentQueue.async {
print("task 1, (i)")
}
}
print("task 1 add complete")
for i in 1...5 {
concurrentQueue.async(flags: .barrier, execute: {
print("task 2, (i)")
})
}
print("task 2 add complete")
for i in 1...5 {
concurrentQueue.async {
print("task 3,(i)")
}
}
print("task 3 add complete")
上面代码的 log 如下:
task 1 add complete
task 1, 2
task 2 add complete
task 1, 1
task 2 add complete
task 1, 4
task 1, 3
task 1, 5
task 2, 1
task 2, 2
task 2, 3
task 2, 4
task 2, 5
task 3,1
task 3,3
task 3,4
task 3,5
task 3,2
由于三项使命我们都是异步增加到行列的,增加后会立即回来,不会堵塞主行列。所以看到三项使命的 add complete 在 task 1 还未履行完的时分就打印了。
依据使命履行的打印信息能够看到,使命一和使命三的履行次序并不是升序的,这说明了使命一和使命三确实是并发履行的。而使命二的履行次序是升序的,并且使命二的一切输出都是在使命一之后,使命三的一切输出在使命二之后。
注意事项:依据 Apple 的官方文档说明,假如你想运用 Barrier 的话,你指定的行列必须是你自己调用 dispatch_queue_create
创立的并发
行列。假如你指定串行行列或者 global 并行行列的话,其作用和 dispatch_async
函数作用共同。
运用场景
一般需求运用到 GCD Barrier 的场景便是多读单写:
- 能够答应读操作并发。
- 读操作和写操作同一时间只能履行一个。
- 写操作和写操作同一时间只能履行一个。
示例代码如下:
class ReadWriteLockByBarrier {
private let queue: DispatchQueue
init() {
queue = DispatchQueue(label: "com.demo.readWriteLock", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)
}
deinit { }
func read<T>(_ closure: () -> T) -> T {
queue.sync {
closure()
}
}
func write(_ closure: @escaping () -> Void) {
queue.async(flags: .barrier) {
closure()
}
}
}
pthread_rwlock_rdlock / pthread_rwlock_wrlock
多读单写的技术完成计划还有别的一种,便是运用 pthread_rwlock_rdlock / pthread_rwlock_wrlock
来完成,具体示例代码如下:
class ReadWriteLock {
private var rwlock = pthread_rwlock_t()
init() {
pthread_rwlock_init(&rwlock, nil)
}
deinit {
pthread_rwlock_destroy(&rwlock)
}
func read<T>(_ closure: () -> T) -> T {
pthread_rwlock_rdlock(&rwlock)
defer { pthread_rwlock_unlock(&rwlock) }
return closure()
}
func write(_ closure: () -> Void) {
pthread_rwlock_wrlock(&rwlock)
defer { pthread_rwlock_unlock(&rwlock) }
closure()
}
}
读写锁供给了一种有用的办法来处理对共享资源的并发拜访。它们答应多个线程一起读取数据,一起保证一次只要一个线程能够写入资源。这会提高功能,由于读取操作不会堵塞其他读取操作,然后减少了线程之间的争用。
读写锁通常供给两种类型的锁:
- 读锁:这些锁能够由多个线程一起获取,答应它们一起读取共享资源。读锁不会堵塞其他读锁,但会阻挠获取写锁。
- 写锁:这些锁供给对共享资源的独占拜访,阻挠其他线程获取读锁和写锁。写锁保证一次只要一个线程能够修改资源,然后防止数据不共同。
总结
- GCD Barrier 能够在并发行列中供给一个机遇来履行同步使命。
- GCD Barrier 指定的行列必须是自己自定义的并发行列,否则作用和
dispatch_async
没有区别。 - 读写锁的两种完成办法:GCD Barrier 和 pthread_rwlock_rdlock / pthread_rwlock_wrlock。