任何人都可以帮助我理解我创建的这段代码吗:
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
调用并发队列的目的,并给我一个例子为什么以及何时需要它?
如果您想演示它们同时运行,您应该单独分派 10 个任务:
let queue = DispatchQueue(label: "queue", attributes: .concurrent)
for i in 0..<10 {
queue.async {
sleep(2)
print(i)
}
}
print("all finished queuing them!")
注:
有 10 个调度到并发队列,而不是一个。
每个调度的任务相对于调度到该队列的其他任务并发运行(这就是为什么我们需要多个调度来说明并发性)。
另请注意,我们异步调度,因为我们不想在调度下一个任务之前调用队列来等待每个已调度的任务。
你问:
所以我最初的想法是:1-10打印应该同时进行,不一定是串行顺序。
因为它们位于单个调度内,所以它们将作为单个任务运行,按顺序运行。您需要将它们放在单独的调度中才能看到它们同时运行。
你继续问:
任何人都可以解释
调用并发队列的目的,并给我一个例子为什么以及何时需要它?sync
sync
与目标队列是串行还是并发无关。 sync
仅指示 calling 线程的行为,即调用者是否应该等待分派任务完成。在这种情况下,你真的不想等待,所以你应该使用async
。
作为一般规则,您应该避免致电
sync
,除非 (a) 您绝对必须这样做; (b) 您愿意阻塞调用线程,直到 sync
任务运行。因此,除了极少数例外,都应该使用 async
。而且,也许不用说,我们永远不会阻塞主线程超过几毫秒。
虽然通常避免在并发调度队列上使用
sync
,但您可能遇到的一个示例是“读写器”同步模式。在这种情况下,“读取”同步发生(因为您需要等待结果),但“写入”与屏障异步发生(因为您不需要等待,但您不希望它与任何事物同时发生)其他在该队列中)。使用 GCD 进行同步(尤其是读写器模式)的详细讨论可能超出了这个问题的范围。但是在网络或 StackOverflow 中搜索“GCD 读写器”,您会找到有关该主题的讨论。)
OSSignposter
在 Instruments 的“兴趣点”工具中创建间隔:
import os.log
private let signposter = OSSignposter(subsystem: "Demo", category: .pointsOfInterest)
final class Demo: Sendable {
func run() {
let queue = DispatchQueue(label: "queue", attributes: .concurrent)
for i in 0..<10 {
queue.async { [self] in
signposter.withIntervalSignpost(#function, id: signposter.makeSignpostID(), "\(i)") {
spin(for: .seconds(2))
}
}
}
signposter.emitEvent("submitted", "finished queuing them!")
}
private func spin(for duration: Duration) {
let start = ContinuousClock.now
while start.duration(to: .now) < duration { }
}
}
当我在仪器中对此进行分析时(例如“产品”»“配置文件”),选择“时间分析器”模板(其中包括“兴趣点”工具),我会看到正在发生的事情的图形时间线:
正如您所指出的,这说明并发任务可能不会按照提交的精确顺序出现。这是因为(a)他们都很快就连续排队了; (b) 它们同时运行:存在一个“竞赛”,即哪个并发运行的线程首先到达日志语句(或“兴趣点”间隔)。
不可以,您必须向
DispatchQueue
添加多个操作,然后它将并行运行多个队列。