使用dispatch_async在Swift中同时分析一个数组

我正尝试使用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) } } 

为了澄清variables名称,下面是它们的描述:

photoAnalyzer是我写的一个名为Analyzer的类的一个实例,它包含处理图像的所有方法。

analyzeColorsAnalyzer类中的一个方法,它执行大部分分析,并返回一个string,其中传入的图像的主色

imageStrips是构成原始图像部分的UIImage数组

colorList是一个string数组,用于存储图像每个部分的analyzeColor方法的返回值。

上面的代码依次运行,因为for循环一次只能从imageList访问一个图像。 我试图做的是同时分析imageStrips每个图像,但我不知道该怎么做。

任何build议将不胜感激。 如果你想看到所有的代码来进一步帮助我,我可以发布一个GitHub链接到它。

编辑这是我更新的代码来同时处理8个处理器。

 dispatch_apply(8, imageQueue) { numStrips -> Void in let color = self.photoAnalyzer.analyzeColors(imageStrips[numStrips]) colorList.append(color) } 

但是,如果我尝试使用超过8个代码,实际上运行速度比顺序执行要慢。

有几种方法可以做到这一点,但是在我们做到这一点之前,有几点看法:

  • 为了尽可能提高性能,如果你做了任何并发处理,请注意,你不能保证它们的完成顺序。 因此,如果一个简单的colorList.append(color)模式出现的顺序很重要,它将不起作用。 你可以预填充一个colorList ,然后让每个迭代只需要colorList[i] = color或者你可以使用一个字典。 (显然,如果订单不重要,那么这不是关键。)

  • 由于这些迭代将同时运行,因此您需要同步更新colorList 。 因此,在后台队列上同时执行昂贵的analyzeColors ,而是使用一个串行队列来更新colorList ,以确保您没有多次更新彼此。

  • 在进行并行处理时,有收益递减点。 例如,将一个复杂的任务分解成2-4个并发循环可能会产生一些性能上的好处,但是如果你开始增加并发线程的数量太多,你会发现这些线程的开销开始对性能产生负面影响。 因此,以不同程度的并发性为基准,不要认为“更multithreading”总是更好。

在如何实现这个方面,有两个基本的技术:

  1. 如果您在“ 并发编程指南:调度队列”指南中看到“ 并行执行循环迭代 ,则会讨论专门为此目的而devise的dispatch_apply并行运行循环。

     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 video blocks和Grand Central Dispatch in Practice中进行了讨论 。

  2. 另一种常见模式是创build一个调度组,将任务分派到使用该组的一个并发队列中,并指定一个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) 

    这种方法避免了完全阻塞主线程,尽pipe你必须小心不要添加太多的并发调度任务(因为工作线程是非常有限的资源)。

在这两个例子中,我创build了一个专用的串行队列来同步更新到colorList 。 这可能是矫枉过正。 如果你没有阻塞主队列(你不应该这样做),你可以把这个同步代码发送到主队列(这是一个串行队列)。 但是,为了这个目的,有一个专门的串行队列可能更为精确。 如果这是我将不断从多个线程进行交互的东西,我会使用读写器模式。 但是对于这种情况,这可能已经足够了。

 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) }); } }