1

谁能帮我理解我创建的这段代码:

let cq = DispatchQueue(label: "downloadQueue", attributes: .concurrent)
cq.sync {
for i in 0..<10 {
    sleep(2)
    print(i)
  }
}

print("all finished!")

输出是串行顺序 1->10,中间等待 2 秒。最后会打印出来all finished

我明白最后一部分。但是我的问题是:

并发队列不应该同时启动多个任务吗?所以我最初的想法是:1-10的打印应该做concurrently,不一定是串行的顺序。

任何人都可以解释sync调用并发队列的目的,并给我一个例子,为什么以及何时需要它?

4

2 回答 2

1

如果你想演示它们同时运行,你应该分别调度这 10 个任务:

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

for i in 0..<10 {
    cq.async {
        sleep(2)
        print(i)
    }
}
print("all finished queuing them!")

笔记:

  • 并发队列有 10 个调度,而不是一个。

    每个分派的任务相对于分派到该队列的其他任务同时运行(这就是为什么我们需要多个分派来说明并发性)。

  • 另请注意,我们是异步调度的,因为我们不想在调度下一个任务之前调用队列来等待每个调度的任务。


你问:

所以我最初的想法是:1-10打印应该同时进行,不一定要按顺序进行。

因为它们在单个调度中,所以它们将作为单个任务运行,按顺序运行。您需要将它们放在单独的调度中以查看它们同时运行。

你继续问:

任何人都可以解释sync调用并发队列的目的,并给我一个例子,为什么以及何时需要它?

与目标队列是串行的还是并发的sync无关。sync唯一规定了调用线程的行为,即调用者是否应该等待分派的任务完成。在这种情况下,你真的不想等待,所以你应该使用async.

作为一般规则,您应该避免跟注,sync除非 (a) 您绝对必须这样做;(b) 你愿意让调用线程阻塞直到sync任务运行。因此,除了极少数例外,应该使用async. 而且,也许不用说,我们从不会阻塞主线程超过几毫秒。

虽然sync通常避免在并发调度队列上使用,但您可能会遇到的一个示例是“读写器”同步模式。在这种情况下,“读取”同步发生(因为您需要等待结果),但“写入”与屏障异步发生(因为您不需要等待,但您不希望它与任何事情同时发生其他在该队列中)。使用 GCD 进行同步的详细讨论(尤其是读写器模式)可能超出了这个问题的范围。但是在 Web 或 StackOverflow 上搜索“GCD reader-writer”,你会发现有关该主题的讨论。)


让我们以图形方式说明我修改后的代码再现,OSLog用于在 Instruments 的“兴趣点”工具中创建间隔:

import os.log

private let log = OSLog(subsystem: "Foo", category: .pointsOfInterest)

class Foo {
    func demonstration() {
        let queue = DispatchQueue(label: "downloadQueue", attributes: .concurrent)

        for i in 0..<10 {
            queue.async { [self] in
                let id = OSSignpostID(log: log)
                os_signpost(.begin, log: log, name: "async", signpostID: id, "%d", i)
                spin(for: 2)
                os_signpost(.end, log: log, name: "async", signpostID: id)
            }
        }
        print("all finished queuing them!")
    }

    func spin(for seconds: TimeInterval) {
        let start = CACurrentMediaTime()
        while CACurrentMediaTime() - start < seconds { }
    }
}

当我在 Instruments(例如“Product”»“Profile”)中对此进行分析时,选择“Time Profiler”模板(其中包括“Points of Interest”工具),我会看到正在发生的事情的图形时间线:

在此处输入图像描述

因此,让我提请您注意上述两个有趣的方面:

  1. 并发队列并发运行任务,但是因为我的 iPhone 的 CPU 只有六个内核,所以实际上只有六个内核可以同时运行。接下来的四个将不得不等到该特定工作线程有可用的核心。

    请注意,此演示之所以有效,是因为我不仅在调用sleep,而是在旋转所需的时间间隔,以更准确地模拟一些缓慢的阻塞任务。对于慢速同步任务,旋转是比它更好的代理sleep

  2. 正如您所指出的,这说明并发任务可能不会按照它们提交的确切顺序出现。这是因为(a)他们都排得太快了;(b) 它们同时运行:对于哪个并发运行的线程首先到达日志记录语句(或“兴趣点”间隔)存在“竞争”。

    归根结底,对于那些同时运行的任务,由于比赛,它们可能看起来没有按顺序运行。这就是并发执行的工作原理。

于 2022-02-23T15:43:13.020 回答
0

不,您必须向 中添加多个操作DispatchQueue,然后它将并行运行多个队列。

于 2022-02-23T09:19:27.267 回答