我正在尝试使用GCD的后台线程同时分析照片。这是我写的代码:
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.value), 0)) {
for (var i = 0; i < 8; i++)
{
let color = self.photoAnalyzer.analyzeColors(imageStrips[i])
colorList.append(color)
}
}
有关变量名称的说明,以下是它们的描述:
photoAnalyzer
是我写的一个名为Analyzer
的类的实例,它包含处理图像的所有方法。
analyzeColors
是Analyzer
类中的一个方法,它执行大部分分析并返回一个带有传入图像主色的字符串
imageStrips
是UIImage
的数组,构成原始图像的部分
colorList
是一个字符串数组,用于存储图像每个部分的analyzeColor
方法的返回值。
上面的代码按顺序运行,因为for
循环一次只能访问imageList
中的一个图像。我想要做的是同时分析imageStrips
中的每个图像,但我不知道如何去做。
任何建议都将不胜感激。如果您希望看到所有代码以进一步帮助我,我可以发布一个GitHub链接。
编辑这是我同时处理8个处理器的更新代码。
dispatch_apply(8, imageQueue) { numStrips -> Void in
let color = self.photoAnalyzer.analyzeColors(imageStrips[numStrips])
colorList.append(color)
}
但是,如果我尝试使用超过8个代码,则实际运行速度比顺序执行速度慢。
答案 0 :(得分:4)
有几种方法可以做到这一点,但在我们开始之前有几点意见:
要尝试最大限度地提高性能,如果您要进行任何并发处理,请注意不保证它们的完成顺序。因此,如果它们出现的顺序很重要,那么简单的colorList.append(color)
模式就不会起作用。您可以预先填充colorList
,然后让每次迭代只执行colorList[i] = color
,也可以使用字典。 (显然,如果订单不重要,那么这并不重要。)
由于这些迭代会同时运行,因此您需要同步colorList
的更新。在后台队列中同时执行昂贵的analyzeColors
,但使用序列队列来更新colorList
,以确保您不会互相踩多个更新。
进行并发处理时,会有收益递减的点。例如,执行复杂任务并将其分解为2-4个并发循环可能会产生一些性能优势,但如果您开始增加并发线程数太多,您会发现这些线程的开销开始产生负面影响影响表现。因此,使用不同程度的并发性对此进行基准测试,并且不要假设&#34;更多线程&#34;总是更好。
就如何实现这一点而言,有两种基本技术:
如果您在并发编程指南:调度队列指南中看到Performing Loop Iterations Concurrently,他们会谈论为此目的而设计的dispatch_apply
来运行for
同时循环。
colorList = [Int](count: 8, repeatedValue: 0) // I don't know what type this `colorList` array is, so initialize this with whatever type makes sense for your app
let queue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)
let qos_attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0)
let syncQueue = dispatch_queue_create("com.domain.app.sync", qos_attr)
dispatch_apply(8, queue) { iteration in
let color = self.photoAnalyzer.analyzeColors(imageStrips[iteration])
dispatch_sync(syncQueue) {
colorList[iteration] = color
return
}
}
// you can use `colorList` here
注意,虽然这些迭代同时运行,但整个dispatch_apply
循环与您启动它的队列同步运行。这意味着您不希望从主线程调用上面的代码(我们永远不想阻止主线程)。因此,可能希望将整个事情发送到一些后台队列。
顺便说一句,dispatch_apply
在WWDC 2011视频Blocks and Grand Central Dispatch in Practice中进行了讨论。
另一种常见模式是创建一个调度组,使用该组将任务分派到并发队列,并指定dispatch_group_notify
以指定完成后要执行的操作。
colorList = [Int](count: 8, repeatedValue: 0) // I don't know what type this `colorList` array is, so initialize this with whatever type makes sense for your app
let group = dispatch_group_create()
let queue = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)
let qos_attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_UTILITY, 0)
let syncQueue = dispatch_queue_create("com.domain.app.sync", qos_attr)
for i in 0 ..< 8 {
dispatch_group_async(group, queue) {
let color = self.photoAnalyzer.analyzeColors(imageStrips[i])
dispatch_sync(syncQueue) {
colorList[i] = color
return
}
}
}
dispatch_group_notify(group, dispatch_get_main_queue()) {
// use `colorList` here
}
// but not here (because the above code is running asynchronously)
这种方法可以避免完全阻塞主线程,但是你必须小心不要添加太多的并发调度任务(因为工作线程是非常有限的资源)。
在这两个例子中,我创建了一个专用的串行队列,用于将更新同步到colorList
。这可能有点矫枉过正。如果您没有阻止主队列(您不应该这样做),您可以将此同步代码分派给主队列(这是一个串行队列)。但是为此目的拥有一个专用的串行队列可能更精确。如果这是我将不断与多个线程进行交互的东西,我会使用读写器模式。但这对于这种情况来说可能已经足够了。
答案 1 :(得分:-1)
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_UTILITY.value), 0)) {
for (var i = 0; i < 8; i++)
{
dispatch_async(dispatch_get_main_queue(), ^(){
//Add method, task you want perform on mainQueue
//Control UIView, IBOutlet all here
let color = self.photoAnalyzer.analyzeColors(imageStrips[i])
colorList.append(color)
});
}
}