swift - 在 GCD 中是通过异步操作 Swift 同步的串行队列

标签 swift multithreading grand-central-dispatch

我正在使用具有 QoS 背景的串行队列

let serialQueue = DispatchQueue(label: "queue1", qos: DispatchQoS.background)

分配一个同步作业,两个异步作业:

func serialTask() {
    serialQueue.sync {
        for i in 0..<10 {
            print("🔷", i)
        }
    }
    serialQueue.async {
        for i in 20..<30 {
            print("⚪️", i)
        }
    }

    serialQueue.async {
        for i in 101..<120 {
            print("🔷", i)
        }
    }
}

所有 3 个作业都在一个接一个地执行同步,但最后两个作业是异步的。异步作业是否在串行队列中同步。

最佳答案

让我看看是否可以阐明 asyncsync 之间的区别。

我将在我的示例中进行一些更改:

  1. 我将使用 Instruments 的“兴趣点”来显示任务何时运行,而不是使用 print 语句。 (参见 WWDC 2019 Getting Started With Instruments。)这样我们就可以以图形方式查看行为。

    我会在 dispatch 某些东西时发布一个简单的“兴趣点”事件路标 (Ⓢ),我会将 dispatch 的任务包裹在“兴趣区域”(一个水平条)中,以图形方式说明某个过程的持续时间。

  2. 我会将您的 for 循环更改为 Thread.sleep(forTimeInterval: 1),模拟一些耗时的过程。如果您只有一个快速的 for 循环,事情会发生得如此之快以至于无法辨别线程真正发生了什么。

所以,考虑:

import os.signpost

private let pointsOfInterest = OSLog(subsystem: "GCD Demo", category: .pointsOfInterest)

func tasks(on queue: DispatchQueue) {
    pointsOfInterestRange(with: "tasks(on:)") {
        os_signpost(.event, log: pointsOfInterest, name: "1") // first Ⓢ
        queue.sync { self.oneSecondProcess(with: "1") }

        os_signpost(.event, log: pointsOfInterest, name: "2") // second Ⓢ
        queue.async { self.oneSecondProcess(with: "2") }

        os_signpost(.event, log: pointsOfInterest, name: "3") // third Ⓢ
        queue.async { self.oneSecondProcess(with: "3") }
    }
}

func oneSecondProcess(with staticString: StaticString) {
    pointsOfInterestRange(with: staticString) {
        Thread.sleep(forTimeInterval: 1)
    }
}

func pointsOfInterestRange(with staticString: StaticString, block: () -> Void) {
    let identifier = OSSignpostID(log: pointsOfInterest)
    os_signpost(.begin, log: pointsOfInterest, name: staticString, signpostID: identifier)
    block()
    os_signpost(.end, log: pointsOfInterest, name: staticString, signpostID: identifier)
}

这就像您的示例,但不是 print 语句,而是路标语句,在 Instruments 的“兴趣点”工具中生成以下图形时间线:

enter image description here

所以,你可以看到:

  1. 底部的 tasks(on:) 函数发出了 sync 调度,第一个 Ⓢ 路标。

  2. 它等待 sync 任务“1”完成,然后再继续,此时它发出两个后续调度,第二个和第三个 Ⓢ 路标(发生得如此之快它们在图中连续重叠)。

  3. 但是 tasks(on:) 不会等待两个 async 任务“2”和“3”完成。一旦完成分派(dispatch)那些 async 任务,它就会立即返回(因此 tasks(on:) 范围会立即停止)。

  4. 由于后台队列是串行的,所以三个派发任务(“1”、“2”、“3”)依次执行,一个接一个。

但是,如果您将其更改为使用并发队列:

let queue = DispatchQueue(label: "...", attributes: .concurrent)

然后您可以看到两个 async 任务现在相互并发运行:

enter image description here

这一次,task(on:) 调度 sync 调用,等待它完成,然后,只有当 sync调用完成后 seriesOfTasks 可以继续分派(dispatch)两个 async 调用(在这种情况下,不等待那些分派(dispatch)任务完成)。

如您所见,asyncsync 的行为是不同的。对于 sync,调用线程将等待分派(dispatch)的任务完成,但对于 async,它不会。


从上面可以得出两个主要结论:

  1. syncasync 的选择决定了当前线程的行为(即它是否应该等待分派(dispatch)的任务)。

    而且,作为一般规则,我们通常会避免在执行任何耗时操作时从主线程调用 sync(因为这最终会阻塞主线程)。

  2. 串行队列与并发队列的选择决定了您分派(dispatch)的工作的行为,即它可以与该队列上的其他任务同时运行,还是一个接一个地连续运行。

关于swift - 在 GCD 中是通过异步操作 Swift 同步的串行队列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49249654/

相关文章:

Android:为什么没有静态方法获取主线程的Handler?

java - 延迟分组网络请求

ios - 检查DISPATCH_QUEUE_PRIORITY_BACKGROUND是否存在的最佳实践?

ios - 将 CGGradient 转换为 CAGradientLayer

java - Swing 中的组件具体什么时候实现的

swift - swift 3 中的类和结构之间的主要区别是什么?

Swift:从 dispatch_async 内部返回结果

objective-c - cocoa :使用dispatch_async错误

ios - viewdidappear 未从选项卡栏 Controller 调用

ios - 自定义单元格插入 TableView 时为空白。应用程序关闭并重新打开时不为空白