Swift调度队列:串行或并行
在我的应用程序中,我必须在后台同时解压多个文件。哪种代码在多个线程上并行执行compressedFiles数组:Swift调度队列:串行或并行,swift,multithreading,concurrency,grand-central-dispatch,Swift,Multithreading,Concurrency,Grand Central Dispatch,在我的应用程序中,我必须在后台同时解压多个文件。哪种代码在多个线程上并行执行compressedFiles数组: for file in compressedFiles { DispatchQueue.global(qos: .userInteractive).async { let work = DispatchGroup() work.enter() file.decompress() work.leave() } } 或: 此外,如果我希望在某个文
for file in compressedFiles {
DispatchQueue.global(qos: .userInteractive).async {
let work = DispatchGroup()
work.enter()
file.decompress()
work.leave()
}
}
或:
此外,如果我希望在某个文件解压缩过程完成后收到通知,如何利用DispatchGroup类?将wait()和notify()放在何处
谢谢。调度组需要在循环外部,每个
输入
都需要在循环内部,但在包含离开
的线程外部。但是整个代码也需要在它自己的调度队列中,因为您不能阻塞(等待)主队列
let queue = DispatchQueue(label:"myqueue")
queue.async {
let work = DispatchGroup()
for file in compressedFiles {
work.enter()
DispatchQueue.global(qos: .userInteractive).async {
file.decompress()
work.leave()
}
}
work.notify... // get on main thread here?
}
第二个示例将按顺序运行它们。它只做一次调度,一个接一个地运行它们。第一个示例将并行运行它们,将每个线程分派到不同的工作线程。但不幸的是,两者都没有正确使用调度组 关于调度组,您应该在循环之前定义它,并在调用
async
之前输入。但是,只有从async
调用中调用异步进程时,才需要手动调用enter
和leave
。但鉴于解压
可能是一个同步过程,您只需将组提供给异步
,它将为您处理所有事情:
let group = DispatchGroup()
for file in compressedFiles {
DispatchQueue.global(qos: .userInteractive).async(group: group) {
file.decompress()
}
}
group.notify(queue: .main) {
// all done
}
但是,并行示例中存在一个更深层次的问题,而不是担心调度组逻辑。具体来说,它会受到线程爆炸的影响,线程爆炸可能会超过CPU上可用的内核数。更糟糕的是,如果您有很多文件要解压缩,您甚至可以超过GCD池中工作线程的有限数量。当这种情况发生时,它可以防止在GCD上运行任何其他服务质量。相反,您希望并行运行它,但您希望在享受并行性的同时将其限制在合理的并发程度,以避免耗尽其他任务的资源
如果您想让它并行运行,但又避免线程爆炸,您通常会使用它。这提供了CPU支持的最大并行性,但防止了线程爆炸可能导致的问题:
DispatchQueue.global(qos: .userInitiated).async {
DispatchQueue.concurrentPerform(iterations: compressedFiles.count) { index in
compressedFiles[index].decompress()
}
DispatchQueue.main.async {
// all done
}
}
这将把并行性限制到设备上内核允许的最大值。它还消除了对调度组的需要
或者,如果您希望享受并行性,但并发度较低(例如,为了让一些内核可用于其他任务,以最小化峰值内存使用率等),您可以使用操作队列和maxConcurrentOperationCount
:
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 4 // a max of 4 decompress tasks at a time
let completion = BlockOperation {
// all done
}
for file in compressedFiles {
let operation = BlockOperation {
file.decompress()
}
completion.addDependency(operation)
queue.addOperation(operation)
}
OperationQueue.main.addOperation(completion)
或者matt指出,在iOS 13(或macOS 10.15)及更高版本中,您可以:
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 4
for file in compressedFiles {
queue.addOperation {
file.decompress()
}
}
queue.addBarrierBlock {
DispatchQueue.main.async {
// all done
}
}
DispatchGroup的启动不应该在for循环中以并行方式创建多个线程,即每个文件一个线程吗?对于notify()?是的,我的回答完全基于OP的原始代码(“哪个代码”)的体系结构和明确的目标(“在多个线程上并行执行压缩文件数组”)。OP没有问这是不是一个好主意。顺便说一句,我可能会建议.userInitiated
或.utility
QoS,而不是.userInteractive
。是的,“新”就是这个意思。我只是说现在它已经存在了,很酷。
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 4
for file in compressedFiles {
queue.addOperation {
file.decompress()
}
}
queue.addBarrierBlock {
DispatchQueue.main.async {
// all done
}
}