什么时候使用GCD队列,何时知道您需要它们?迅速 [英] When are GCD queues used and when do you know you need them? Swift

查看:48
本文介绍了什么时候使用GCD队列,何时知道您需要它们?迅速的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在了解了并发和串行队列,同步和异步之后,我想我对如何创建队列及其执行顺序有所了解.我的问题是,在我所见过的任何教程中,都没有实际上告诉您许多用例.例如:

After reading about Concurrent and Serial queues, sync and async, I think I have an idea about how to create queues and the order they are executed in. My problem is that in any of the tutorials I have seen, none of them actually tell you many use cases. For example:

我有一个网络管理员,该管理员使用URLSessions并序列化json以向我的api发送请求.将其包装在.utility队列中或在.userInitiated中是否有意义,或者我只是不将其包装在队列中.

I have a network manager that uses URLSessions and serializes json to send a request to my api. Does it make sense to wrap it in a .utility Queue or in a .userInitiated or do I just don't wrap it in a queue.

let task = LoginTask(username: username, password: password)

let networkQueue = DispatchQueue(label: "com.messenger.network", 
qos: DispatchQoS.userInitiated)

networkQueue.async {
    task.dataTask(in: dispatcher) { (user, httpCode, error) in
        self.presenter?.loginUserResponse(user: user, httpCode: httpCode, error: error)
    }
}

我的问题是:是否有任何指南可用来了解何时需要使用队列,因为我无法在任何地方找到此信息.我意识到苹果非常模糊地提供了示例用法

My question is: Is there any guidlines I can follow to know when there is a need to use queues or not because I cant find this information anywhere. I realise apple provides example usage howver it is very vague

推荐答案

在许多用例中都使用了调度队列,因此很难枚举它们,但是两个非常常见的用例如下:

Dispatch queues are used in a multitude of use cases, so it's hard to enumerate them, but two very common use cases are as follows:

  1. 您有一些昂贵和/或耗时的过程要在当前线程以外的其他线程上运行.当您在主线程上并且想要在后台线程上运行某些内容时,通常会使用它.

  1. You have some expensive and/or time-consuming process that you want to run on some thread other than the current thread. Often this is used when you're on the main thread and you want to run something on a background thread.

一个很好的例子是图像处理,这是一个众所周知的计算(和内存)密集的过程.因此,您将创建一个图像处理队列,然后将每个图像处理任务分派到该队列.您还可以在完成UI更新后将其分发回主队列(因为所有UI更新必须在主线程上进行).常见的模式是:

A good example of this would be image manipulation, which is a notoriously computationally (and memory) intensive process. So, you'd create a queue for image manipulation and then you'd dispatch each image manipulation task to that queue. You might also dispatch the UI update when it's done back to the main queue (because all UI updates must happen on the main thread). A common pattern would be:

imageQueue.async {
    // manipulate the image here

    // when done, update the UI:

    DispatchQueue.main.async {
        // update the UI and/or model objects on the main thread
    }
}

  • 您想要同步一些共享资源(它可以是一个简单的变量,也可以是与某些其他共享资源(例如文件或数据库)的交互),而不管是从哪个线程调用它.这通常是使某些本质上不是线程安全的事物以线程安全的方式行为的更广泛策略的一部分.

  • You have some shared resource (it could be a simple variable, it could be some interaction with some other shared resource like a file or database) that you want to synchronize regardless of from which thread to invoke it. This is often part of a broader strategy of making something that is not inherently thread-safe behave in a thread safe manner.

    调度队列的优点在于,它极大地简化了编写多线程代码的过程,否则该技术非常复杂.

    The virtue of dispatch queues is that it greatly simplifies writing multi-threaded code, an otherwise very complicated technology.

    问题是,您的示例(发起网络请求)已经在后台线程上运行了请求,并且URLSession为您管理了所有这些操作,因此为此使用队列几乎没有价值.

    The thing is that your example, initiating a network request, already runs the request on a background thread and URLSession manages all of this for you, so there's little value in using queues for that.

    为了全面披露,在上述基本调度队列之上和之外,直接(例如调度组或调度源)或间接(例如操作队列)使用GCD的各种不同工具令人惊讶:

    In the interest of full disclosure, there is a surprising of variety of different tools using GCD directly (e.g. dispatch groups or dispatch sources) or indirectly (e.g. operation queues) above and beyond the basic dispatch queues discussed above:

    • 调度组:有时,您将启动一系列异步任务,并且希望在它们全部完成后得到通知.您可以使用调度组(有关随机示例,请参见 https://stackoverflow.com/a/28101212/1271826 ) .这使您无需跟踪自己何时完成所有这些任务.

    • Dispatch groups: Sometimes you will initiate a series of asynchronous tasks and you want to be notified when they're all done. You can use a dispatch group (see https://stackoverflow.com/a/28101212/1271826 for a random example). This eliminates you from needing to keep track of when all of these tasks are done yourself.

    分派应用"(现在称为concurrentPerform):有时,当您运行一些大型并行任务时,您想要合理地使用尽可能多的线程.因此,concurrentPerform可以让您有效地并行执行for循环,而Apple已针对特定设备的内核和CPU数量对其进行了优化,而同时又不会一次充斥太多并发任务,从而耗尽了有限的数量工人线程.有关在其中运行for循环的示例,请参见 https://stackoverflow.com/a/39949292/1271826 .平行.

    Dispatch "apply" (now called concurrentPerform): Sometimes when you're running some massively parallel task, you want to use as many threads as you reasonably can. So concurrentPerform lets you effectively perform a for loop in parallel, and Apple has optimized it for the number of cores and CPUs your particular device, while not flooding it with too many concurrent tasks at any one time, exhausting the limited number of worker threads. See the https://stackoverflow.com/a/39949292/1271826 for an example of running a for loop in parallel.

    调度源:

    • 例如,如果您有一些正在做的大量后台任务,并且想用进度来更新UI,则有时这些UI更新的速度可能比UI处理它们的速度更快.因此,您可以使用调度源(a DispatchSourceUserDataAdd)将后台进程与UI更新解耦.有关示例,请参见上述 https://stackoverflow.com/a/39949292/1271826 .

    • For example, if you have some background task that is doing a lot of work and you want to update the UI with the progress, sometimes those UI updates can come more quickly than the UI can handle them. So you can use a dispatch source (a DispatchSourceUserDataAdd) to decouple the background process from the UI updates. See aforementioned https://stackoverflow.com/a/39949292/1271826 for an example.

    传统上,Timer在主运行循环上运行.但是有时您想在后台线程上运行它,但是使用Timer进行操作很复杂.但是您可以使用DispatchSourceTimer(GCD计时器)在主队列以外的其他队列上运行计时器.有关如何创建和使用调度计时器的示例,请参见 https://stackoverflow.com/a/38164203/1271826 .调度计时器还可以用于避免某些基于targetTimer对象容易引入的强大参考周期.

    Traditionally, a Timer runs on the main run loop. But sometimes you want to run it on a background thread, but doing that with a Timer is complicated. But you can use a DispatchSourceTimer (a GCD timer) to run a timer on a queue other than the main queue. See https://stackoverflow.com/a/38164203/1271826 for example of how to create and use a dispatch timer. Dispatch timers also can be used to avoid some of the strong reference cycles that are easily introduced with target-based Timer objects.

    障碍:有时在使用并发队列时,您希望大多数事物并发运行,而对于其他事物,则要相对于队列中的其他所有事物依次运行.障碍是一种表示将任务添加到队列中,但要确保它不会相对于该队列中的其他任何对象同时运行"的方式.

    Barriers: Sometimes when using a concurrent queue, you want most things to run concurrently, but for other things to run serially with respect to everything else on the queue. A barrier is a way to say "add this task to the queue, but make sure it doesn't run concurrently with respect to anything else on that queue."

    障碍的一个例子是读写器模式,其中从某个内存资源进行的读取可以相对于所有其他读取同时发生,但是任何写入都不得相对于队列中的其他任何内容同时发生.请参阅 https://stackoverflow.com/a/28784770/1271826

    An example of a barrier is the reader-writer pattern, where reading from some memory resource can happen concurrently with respect to all other reads, but any writes must not happen concurrently with respect to anything else on the queue. See https://stackoverflow.com/a/28784770/1271826 or https://stackoverflow.com/a/45628393/1271826.

    调度信号量:有时您需要让运行在不同线程上的两个任务相互通信.您可以将信号量用于一个线程,以等待"另一线程的信号".

    Dispatch semaphores: Sometimes you need to let two tasks running on separate threads communicate to each other. You can use semaphores for one thread to "wait" for the "signal" from another.

    信号量的一种常见应用是使固有的异步任务以更同步的方式运行.

    One common application of semaphores is to make an inherently asynchronous task behave in a more synchronous manner.

    networkQueue.async {
        let semaphore = DispatchSemaphore(0)
        let task = session.dataTask(with: url) { data, _, error in
            // process the response
    
            // when done, signal that we're done
            semaphore.signal()
        }
        task.resume()
        semaphore.wait(timeout: .distantFuture)
    }
    

    这种方法的优点在于,在完成异步网络请求之前,已分派的任务不会完成.因此,如果您需要发出一系列网络请求,但又不想让它们同时运行,则信号量就可以完成.

    The virtue of this approach is that the dispatched task won't finish until the asynchronous network request is done. So if you needed to issue a series of network requests, but not have them run concurrently, semaphores can accomplish that.

    不过,应谨慎使用信号量,因为它们本质上效率低下(通常阻塞一个线程等待另一个线程).另外,请确保不要从主线程发出wait信号灯(因为您要打败执行异步任务的目的).这就是为什么在上面的示例中,我等待networkQueue而不是主队列.所有这些都已经说过了,通常有比信号量更好的技术,但是有时它是有用的.

    Semaphores should be used sparingly, though, because they're inherently inefficient (generally blocking one thread waiting for another). Also, make sure you never wait for a semaphore from the main thread (because you're defeating the purpose of having the asynchronous task). That's why in the above example, I'm waiting on the networkQueue, not the main queue. All of this having been said, there's often better techniques than semaphores, but it is sometimes useful.

    操作队列:操作队列建立在GCD调度队列的顶部,但具有一些有趣的优点,包括:

    Operation queues: Operation queues are built on top of GCD dispatch queues, but offer some interesting advantages including:

    • 能够将固有的异步任务包装在自定义的Operation子类中. (这避免了我之前讨论的信号量技术的缺点.)通常在后台线程上运行固有的同步任务时使用调度队列,但是有时您想管理一堆本身是异步的任务.一个常见的示例是将异步网络请求包装在Operation子类中.

    • The ability to wrap an inherently asynchronous task in a custom Operation subclass. (This avoids the disadvantages of the semaphore technique I discussed earlier.) Dispatch queues are generally used when running inherently synchronous tasks on a background thread, but sometimes you want to manage a bunch of tasks that are, themselves, asynchronous. A common example is the wrapping of asynchronous network requests in Operation subclass.

    轻松控制并发程度的能力.调度队列可以是串行的,也可以是并发的,但是将控制机制设计为例如说彼此并发地运行已排队的任务,但在任何给定时间不超过四个"是很麻烦的.使用maxConcurrentOperationCount,操作队列使此操作变得更加容易. (例如,请参见 https://stackoverflow.com/a/27022598/1271826 .)

    The ability to easily control the degree of concurrency. Dispatch queues can be either serial or concurrent, but it's cumbersome to design the control mechanism to, for example, to say "run the queued tasks concurrent with respect to each other, but no more than four at any given time." Operation queues make this much easier with the use of maxConcurrentOperationCount. (See https://stackoverflow.com/a/27022598/1271826 for an example.)

    能够在各种任务之间建立依赖关系的功能(例如,您可能有一个用于下载图像的队列和另一个用于处理图像的队列).使用操作队列,您可以执行一个操作来下载图像,而执行另一个操作来处​​理图像,并使后者取决于前者的完成.

    The ability to establish dependencies between various tasks (e.g. you might have a queue for downloading images and another queue for manipulating the images). With operation queues you can have one operation for the downloading of an image and another for the processing of the image, and you can make the latter dependent upon the completion of the former.

    还有许多其他与GCD相关的应用程序和技术,但我经常使用这些.

    There are lots of other GCD related applications and technologies, but these are a few that I use with some frequency.

    这篇关于什么时候使用GCD队列,何时知道您需要它们?迅速的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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