使用dispatch_async在Swift中同时分析数组 [英] Use dispatch_async to analyze an array concurrently in Swift

查看:391
本文介绍了使用dispatch_async在Swift中同时分析数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用GCD的后台线程同时分析照片。这是我写的代码:

I am trying to analyze a photo concurrently using a background thread from GCD. Here is the code I have written:

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

有关变量名称的说明,以下是它们的描述:

For clarification on the variable names, here are their descriptions:

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

photoAnalyzer is an instance of a class I wrote called Analyzer that holds all of the methods to process the image.

analyzeColors 是<$ c $内的方法c> Analyzer 执行大部分分析并返回带有传入图像主色的字符串

analyzeColors is a method inside the Analyzer class that does the majority of the analysis and returns a string with the dominant color of the passed in image

imageStrips 是一个 UIImage 的数组,它构成原始图像的部分

imageStrips is an array of UIImage's that make up the portions of the original image

colorList 是一个存储返回的字符串数组n图像每个部分的 analyzeColor 方法的值。

colorList is an array of strings that stores the return values of the analyzeColor method for each portion of the image.

上面的代码从<$开始顺序运行c $ c> for 循环一次只能访问 imageList 中的一个图像。我想要做的是同时分析 imageStrips 中的每个图像,但我不知道该怎么做。

The above code runs sequentially since the for loop only accesses one image from the imageList at a time. What I am trying to do is analyze each image in imageStrips concurrently, but I had no idea how to go about doing that.

我们将非常感谢任何建议。如果您希望看到所有代码以进一步帮助我,我可以发布一个GitHub链接。

Any suggestions would be greatly appreciated. And if you would like to see all of the code to further help me I can post a GitHub link to it.

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

EDIT This is my updated code to handle 8 processors concurrently.

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

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

However, if I try to use more than 8 the code actually runs slower than it does sequentially.

推荐答案

有几种方法可以做到这一点,但在我们开始之前还有一些观察结果:

There are a couple of ways of doing this, but there are a couple of observations before we get to that:


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

  • To try to maximize performance, if you do any concurrent processing, be aware that you are not guaranteed the order in which they will complete. Thus a simple colorList.append(color) pattern won't work if the order that they appear is important. You can either prepopulate a colorList and then have each iteration simply do colorList[i] = color or you could use a dictionary. (Obviously, if order is not important, then this is not critical.)

因为这些迭代将同时运行,所以您需要同步更新 colorList 。那么在后台队列上同时使用昂贵的 analyzeColors ,但使用串行队列来更新 colorList ,以确保你彼此之间没有多个更新。

Because these iterations will be running concurrently, you'll need to synchronize your updating of colorList. So do your expensive analyzeColors concurrently on background queue, but use a serial queue for the updating of colorList, to ensure you don't have multiple updates stepping over each other.

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

When doing concurrent processing, there are points of diminishing returns. For example, taking a complex task and breaking it into 2-4 concurrent loops might yield some performance benefit, but if you start increasing the number of concurrent threads too much, you'll find that the overhead of these threads starts to adversely affect performance. So benchmark this with different degrees of concurrency and don't assume that "more threads" is always better.

In如何实现这一目标,有两种基本技巧:

In terms of how to achieve this, there are two basic techniques:


  1. 如果你看到同时执行循环迭代并发编程指南:调度队列指南中,他们讨论 dispatch_apply ,它是专门为此目的而设计的,用于运行 for 并发循环。

  1. If you see Performing Loop Iterations Concurrently in the Concurrency Programming Guide: Dispatch Queues guide, they talk about dispatch_apply, which is designed precisely for this purpose, to run for loops concurrently.

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 循环与您启动它的队列同步运行。这意味着您不希望从主线程调用上面的代码(我们永远不想阻止主线程)。因此,可能希望将整个事情发送到某个后台队列。

Note, while these iterations run concurrently, the whole dispatch_apply loop runs synchronously with respect to the queue from which you initiated it. This means that you will not want to call the above code from the main thread (we never want to block the main thread). So will likely want to dispatch this whole thing to some background queue.

顺便说一句, dispatch_apply 在下面讨论WWDC 2011视频 Block and Grand Central Dispatch in Practice

By the way, dispatch_apply is discussed in WWDC 2011 video Blocks and Grand Central Dispatch in Practice.

另一种常见模式是创建一个调度组,使用该组将任务分派到并发队列,并指定 dispatch_group_notify 指定完成后你想做什么。

Another common pattern is to create a dispatch group, dispatch the tasks to a concurrent queue using that group, and specify a dispatch_group_notify to specify what you want to do when it's done.

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)

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

This approach avoids blocking the main thread altogether, though you have to be careful to not add too many concurrent dispatched tasks (as the worker threads are a very limited resource).

在这两个例子中,我创建了一个专用的串行队列,用于将更新同步到 colorList 。这可能是矫枉过正的。如果您没有阻塞主队列(无论如何都不应该这样做),您可以将此同步代码分派给主队列(这是一个串行队列)。但是为此目的拥有一个专用的串行队列可能更精确。如果这是我将不断与多个线程进行交互的东西,我将使用读写器模式。但对于这种情况,这可能已经足够了。

In both of these examples, I created a dedicated serial queue for synchronizing the updates to colorList. That may be overkill. If you're not blocking the main queue (which you shouldn't do anyway), you could dispatch this synchronization code to the main queue (which is a serial queue). But it's probably more precise to have a dedicated serial queue for this purpose. And if this was something that I was going to be interacting with from multiple threads constantly, I'd use a reader-writer pattern. But this is probably good enough for this situation.

这篇关于使用dispatch_async在Swift中同时分析数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆