使用dispatch_async在Swift

时间:2015-04-27 03:44:53

标签: ios multithreading swift grand-central-dispatch dispatch-async

我正在尝试使用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的类的实例,它包含处理图像的所有方法。

analyzeColorsAnalyzer类中的一个方法,它执行大部分分析并返回一个带有传入图像主色的字符串

imageStripsUIImage的数组,构成原始图像的部分

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个代码,则实际运行速度比顺序执行速度慢。

2 个答案:

答案 0 :(得分:4)

有几种方法可以做到这一点,但在我们开始之前有几点意见:

  • 要尝试最大限度地提高性能,如果您要进行任何并发处理,请注意不保证它们的完成顺序。因此,如果它们出现的顺序很重要,那么简单的colorList.append(color)模式就不会起作用。您可以预先填充colorList,然后让每次迭代只执行colorList[i] = color,也可以使用字典。 (显然,如果订单不重要,那么这并不重要。)

  • 由于这些迭代会同时运行,因此您需要同步colorList的更新。在后台队列中同时执行昂贵的analyzeColors,但使用序列队列来更新colorList,以确保您不会互相踩多个更新。

    < / LI>
  • 进行并发处理时,会有收益递减的点。例如,执行复杂任务并将其分解为2-4个并发循环可能会产生一些性能优势,但如果您开始增加并发线程数太多,您会发现这些线程的开销开始产生负面影响影响表现。因此,使用不同程度的并发性对此进行基准测试,并且不要假设&#34;更多线程&#34;总是更好。

就如何实现这一点而言,有两种基本技术:

  1. 如果您在并发编程指南:调度队列指南中看到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中进行了讨论。

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

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

  3. 在这两个例子中,我创建了一个专用的串行队列,用于将更新同步到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)
    });
    }
}