# 26.栅栏函数

## 前言

最近在 `Swift` 中想用到`栅栏函数`，结果犯了难，怎么调用写不出来，也是稍微查了一下才知道怎么样用。

顺手总结回顾一下`栅栏函数`吧。

## 什么是栅栏函数

> [Dispatch Barrier](https://developer.apple.com/documentation/dispatch/dispatch_barrier)

我们瞅瞅官方文档的定义：***在并发调度队列中执行的任务的同步点***

说人话：***可以控制\*\*\*\*`并发对列`\*\*\*\*任务的执行顺序***

### 1.1 常用的API

* dispatch\_barrier\_async
  * 前面的任务执行完才会执行这里面的
* dispatch\_barrier\_sync
  * 阻塞线程，前面的任务执行完才会执行这里面的，然后再执行后面的

下面我们看看实际的使用效果

## 二、 Swift 中该怎么写栅栏函数呢

`dispatch_barrier_async` / `dispatch_barrier_sync` 这两个 API 是用不了的。

![1](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2Fb77779e62c5c18c7dd47914e2e8532a8fa4dd6c4.png?generation=1631347420687332\&alt=media)

### 2.1 创建并发队列

根据提示，我们先创建一个队列：

```swift
let queue = DispatchQueue.init(label: "RyukieQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)
```

* 参数
  * label
    * 队列名字
  * qos
    * 优先级
  * attributes
    * 属性列表，可以在这里设置同步异步（添加了 `.concurrent` 就是并发对列，否则就是穿行队列）
  * autoreleaseFrequency
    * block 内制动释放的频率
  * target
    * 想要 block 在哪个对列执行，默认是当前对列

### 2.2 创建栅栏函数

通过文档发现这个[API](https://developer.apple.com/documentation/dispatch/dispatchqueue/2016098-async)

* 参数
  * group
    * 调度组
  * qos
    * 优先级
  * flags
    * 附加属性，可以在这里设置为栅栏函数
  * work
    * block

```swift
queue.async(group: nil, qos: .default, flags: .barrier) {
    xxx
}
```

## 三、 异步栅栏函数 + 自定义队列

```swift
let queue = DispatchQueue.init(label: "RyukieQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)

queue.async {
    print("1: \(Thread.current)")
    sleep(1)
}
// 异步栅栏函数
queue.async(group: nil, qos: .default, flags: .barrier) {
    print("barrier - async: \(Thread.current)")
    for _ in 0...100 {
        print("...")
    }
}

queue.async {
    print("2: \(Thread.current)")
}

queue.async {
    print("3: \(Thread.current)")
}

queue.async {
    print("4: \(Thread.current)")
}

print("=========: \(Thread.current)")
```

这里的代码你觉得会是怎么样的执行结果呢？

***输出：***

```
1: <NSThread: 0x6000018f1040>{number = 6, name = (null)}
=========: <NSThread: 0x6000018b0800>{number = 1, name = main}
barrier - async: <NSThread: 0x6000018f1040>{number = 6, name = (null)}
...

...
...
4: <NSThread: 0x6000018f6fc0>{number = 4, name = (null)}
3: <NSThread: 0x6000018f0540>{number = 5, name = (null)}
2: <NSThread: 0x6000018f1040>{number = 6, name = (null)}
```

### 3.1 流程分析

![2](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2F3e04778ec40e70ed3b176f883a2fc9cafdbb2dcf.png?generation=1631347420517973\&alt=media)

* 图中蓝线代表了代码执行的顺序
* `2/3/4`异步任务顺序不一定相信大家很好理解
* 队列中存在了 `异步栅栏函数` 它之后的任务将不会得到调度
* `======` 为队列之外的代码，所以不受 `异步栅栏函数` 影响，因为其他都是异步的任务，它输出位置不一定但是极大概率在 `异步栅栏函数` 之前执行

## 四、 同步栅栏函数 + 自定义队列

代码稍作调整：

```swift
let queue = DispatchQueue.init(label: "RyukieQueue", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem, target: nil)

queue.async {
    print("1: \(Thread.current)")
    sleep(1)
}

// 同步栅栏函数
queue.sync(flags: .barrier) {
    print("barrier - sync: \(Thread.current)")
    for _ in 0...100 {
        print("...")
    }
}

queue.async {
    print("2: \(Thread.current)")
}

queue.async {
    print("3: \(Thread.current)")
}

queue.async {
    print("4: \(Thread.current)")
}

print("=========: \(Thread.current)")
```

这里的代码你觉得会是怎么样的执行结果呢？

***输出：***

```
1: <NSThread: 0x6000038b3640>{number = 13, name = (null)}
barrier - sync: <NSThread: 0x6000038f83c0>{number = 1, name = main}
...
...
...
...

...
...
...
=========: <NSThread: 0x6000038f83c0>{number = 1, name = main}
2: <NSThread: 0x6000038b3640>{number = 13, name = (null)}
4: <NSThread: 0x6000038b3640>{number = 13, name = (null)}
3: <NSThread: 0x6000038ddb00>{number = 11, name = (null)}
```

### 4.1 流程分析

![3](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2F398ef2dd21e095fbe47e46201b9833506ce6a4b2.png?generation=1631347420330607\&alt=media)

* 图中蓝线代表了代码执行的顺序
* `同步栅栏函数` ，会阻断当前代码区间线程代码的执行
* 如图所示，只有当 `同步栅栏函数` 内的任务执行完成，才会继续外部后续代码
* 所以 `====` & `2/3/4` 一定在 `同步栅栏函数` 后

## 五、 全局并发队列

前面有提到，栅`栏函数`只能在`并发队列`中使用，但是上面都是用到了`自定义并发队列`。那么全局并发队列可以用么？来验证一下吧

### 5.1 异步栅栏函数 + 全局并发队列

![4](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2F64edf30324b7660dcf0843269fc716ba22d9f1a4.png?generation=1631347420830199\&alt=media)

通过反复调试，发现栅栏函数失效了

```
2: <NSThread: 0x6000026bd280>{number = 7, name = (null)}
=========: <NSThread: 0x6000026f0180>{number = 1, name = main}
3: <NSThread: 0x6000026bc540>{number = 3, name = (null)}
1: <NSThread: 0x6000026be080>{number = 6, name = (null)}
4: <NSThread: 0x6000026f1bc0>{number = 5, name = (null)}
barrier - async: <NSThread: 0x6000026bc540>{number = 3, name = (null)}
...
...
...
```

下面再试试同步的

### 5.1 同步栅栏函数 + 全局并发队列

![5](https://4193904735-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MI8JgbGh3U6X_oedqkm%2Fsync%2F187bd8e28eefb4d154d352c3c5d702c20116c936.png?generation=1631347420118351\&alt=media)

同样失去了原本的效果

```
barrier - sync: <NSThread: 0x60000156c800>{number = 1, name = main}
...

...
...
...
...
=========: <NSThread: 0x60000156c800>{number = 1, name = main}
1: <NSThread: 0x60000152a280>{number = 9, name = (null)}
2: <NSThread: 0x60000152c4c0>{number = 4, name = (null)}
4: <NSThread: 0x600001528700>{number = 10, name = (null)}
3: <NSThread: 0x60000152ec40>{number = 11, name = (null)}
```

### 5.2 思考

全局对列作为全局的对列，会有很多任务，很多是我们不可控的，如果被阻拦下来了会导致很多未知问题。所以这里设计者就将其设计为六不可使用栅栏函数👍。

> 根据题的逻辑只有源码能够带来答案，有兴趣可以去[GCD源码：libdispatch](https://opensource.apple.com/tarballs/libdispatch/)探索下

## 参考

* [DispatchQueue](https://developer.apple.com/documentation/dispatch/dispatchqueue)
* [GCD源码：libdispatch](https://opensource.apple.com/tarballs/libdispatch/)
