前语

iOS 中,我们能够运用 Barrier 来对并发行列中的一个或者多个使命进行同步调度。也便是说,它答应你将线程不安全的目标转换为线程安全的目标。它为在并发调度行列中履行的代码块创立一个同步点。

了解 Swift 中的 DispatchQueue 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。