ios GCD 中的并发队列与串行队列
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19179358/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Concurrent vs serial queues in GCD
提问by Bogdan Alexandru
I'm struggling to fully understand the concurrent and serial queues in GCD. I have some issues and hoping someone can answer me clearly and at the point.
我正在努力完全理解 GCD 中的并发和串行队列。我有一些问题,希望有人能清楚地回答我。
I'm reading that serial queues are created and used in order to execute tasks one after the other. However, what happens if:
- I create a serial queue
- I use
dispatch_async
(on the serial queue I just created) three times to dispatch three blocks A,B,C
Will the three blocks be executed:
in order A,B,C because the queue is serial
OR
- concurrently (in the same time on parralel threads) because I used ASYNC dispatch
I'm reading that I can use
dispatch_sync
on concurrent queues in order to execute blocks one after the other. In that case, WHY do serial queues even exist, since I can always use a concurrent queue where I can dispatch SYNCHRONOUSLY as many blocks as I want?Thanks for any good explanation!
我正在读到创建和使用串行队列是为了一个接一个地执行任务。但是,如果出现以下情况会发生什么:
- 我创建了一个串行队列
- 我使用
dispatch_async
(在我刚刚创建的串行队列上)三次来分派三个块 A、B、C
会不会执行三个block:
按 A、B、C 顺序排列,因为队列是串行的
或者
- 同时(在并行线程上同时)因为我使用了 ASYNC 调度
我正在阅读我可以
dispatch_sync
在并发队列上使用的内容,以便一个接一个地执行块。在那种情况下,为什么串行队列甚至存在,因为我总是可以使用并发队列,在那里我可以根据需要同步调度尽可能多的块?感谢任何好的解释!
回答by Stephen Darlington
A simple example: you have a block that takes a minute to execute. You add it to a queue from the main thread. Let's look at the four cases.
一个简单的例子:你有一个需要一分钟才能执行的块。您将其从主线程添加到队列中。我们来看看这四种情况。
- async - concurrent: the code runs on a background thread. Control returns immediately to the main thread (and UI). The block can't assume that it's the only block running on that queue
- async - serial: the code runs on a background thread. Control returns immediately to the main thread. The block canassume that it's the only block running on that queue
- sync - concurrent: the code runs on a background thread but the main thread waits for it to finish, blocking any updates to the UI. The block can't assume that it's the only block running on that queue (I could have added another block using async a few seconds previously)
- sync - serial: the code runs on a background thread but the main thread waits for it to finish, blocking any updates to the UI. The block canassume that it's the only block running on that queue
- 异步 - 并发:代码在后台线程上运行。控制立即返回到主线程(和 UI)。该块不能假设它是该队列上运行的唯一块
- 异步 - 串行:代码在后台线程上运行。控制立即返回到主线程。该块可以假设它是该队列上运行的唯一块
- 同步 - 并发:代码在后台线程上运行,但主线程等待它完成,阻止对 UI 的任何更新。该块不能假设它是该队列上运行的唯一块(我可以在几秒钟前使用 async 添加另一个块)
- 同步 - 串行:代码在后台线程上运行,但主线程等待它完成,阻止对 UI 的任何更新。该块可以假设它是该队列上运行的唯一块
Obviously you wouldn't use either of the last two for long running processes. You normally see it when you're trying to update the UI (always on the main thread) from something that may be running on another thread.
显然,您不会将最后两个中的任何一个用于长时间运行的进程。当您尝试从可能在另一个线程上运行的某些内容更新 UI(始终在主线程上)时,您通常会看到它。
回答by Anish Parajuli ?
Here are a couple of experiments that i have done to make me understand about these serial
, concurrent
queues with Grand Central Dispatch
.
这是我所做的一些实验,以使我了解这些serial
,concurrent
使用Grand Central Dispatch
.
func doLongAsyncTaskInSerialQueue() {
let serialQueue = DispatchQueue(label: "com.queue.Serial")
for i in 1...5 {
serialQueue.async {
if Thread.isMainThread{
print("task running in main thread")
}else{
print("task running in background thread")
}
let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
let _ = try! Data(contentsOf: imgURL)
print("\(i) completed downloading")
}
}
}
Task will run in different thread(other than main thread) when you use async in GCD. Async means execute next line do not wait until the block executes which results non blocking main thread & main queue. Since its serial queue, all are executed in the order they are added to serial queue.Tasks executed serially are always executed one at a time by the single thread associated with the Queue.
当您在 GCD 中使用 async 时,任务将在不同的线程(主线程除外)中运行。异步意味着执行下一行不要等到块执行这导致非阻塞主线程和主队列。由于它的串行队列,所有的都按照它们添加到串行队列的顺序执行。串行执行的任务总是由与队列关联的单个线程一次执行一个。
func doLongSyncTaskInSerialQueue() {
let serialQueue = DispatchQueue(label: "com.queue.Serial")
for i in 1...5 {
serialQueue.sync {
if Thread.isMainThread{
print("task running in main thread")
}else{
print("task running in background thread")
}
let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
let _ = try! Data(contentsOf: imgURL)
print("\(i) completed downloading")
}
}
}
Task may run in main thread when you use sync in GCD. Sync runs a block on a given queue and waits for it to complete which results in blocking main thread or main queue.Since the main queue needs to wait until the dispatched block completes, main thread will be available to process blocks from queues other than the main queue.Therefore there is a chance of the code executing on the background queue may actually be executing on the main thread Since its serial queue, all are executed in the order they are added(FIFO).
当您在 GCD 中使用同步时,任务可能会在主线程中运行。同步在给定的队列上运行一个块并等待它完成,这会导致主线程或主队列阻塞。由于主队列需要等到分派的块完成,主线程将可用于处理来自队列以外的队列的块主队列。因此后台队列上执行的代码有可能实际上是在主线程上执行的,因为它的串行队列,都是按照添加的顺序执行的(FIFO)。
func doLongASyncTaskInConcurrentQueue() {
let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
for i in 1...5 {
concurrentQueue.async {
if Thread.isMainThread{
print("task running in main thread")
}else{
print("task running in background thread")
}
let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
let _ = try! Data(contentsOf: imgURL)
print("\(i) completed downloading")
}
print("\(i) executing")
}
}
Task will run in background thread when you use async in GCD. Async means execute next line do not wait until the block executes which results non blocking main thread. Remember in concurrent queue, task are processed in the order they are added to queue but with different threads attached to the queue. Remember they are not supposed to finish the task as the order they are added to the queue.Order of task differs each time threads are created as necessarily automatically.Task are executed in parallel. With more than that(maxConcurrentOperationCount) is reached, some tasks will behave as a serial until a thread is free.
当您在 GCD 中使用 async 时,任务将在后台线程中运行。异步意味着执行下一行不要等到块执行这导致非阻塞主线程。请记住,在并发队列中,任务按照它们添加到队列的顺序进行处理,但不同的线程附加到队列中。请记住,它们不应该按照将它们添加到队列中的顺序来完成任务。每次创建线程时,任务的顺序都不同,因为必须自动创建。任务是并行执行的。超过 (maxConcurrentOperationCount) 时,某些任务将表现为串行,直到线程空闲。
func doLongSyncTaskInConcurrentQueue() {
let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
for i in 1...5 {
concurrentQueue.sync {
if Thread.isMainThread{
print("task running in main thread")
}else{
print("task running in background thread")
}
let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
let _ = try! Data(contentsOf: imgURL)
print("\(i) completed downloading")
}
print("\(i) executed")
}
}
Task may run in main thread when you use sync in GCD. Sync runs a block on a given queue and waits for it to complete which results in blocking main thread or main queue.Since the main queue needs to wait until the dispatched block completes, main thread will be available to process blocks from queues other than the main queue.Therefore there is a chance of the code executing on the background queue may actually be executing on the main thread. Since its concurrent queue, tasks may not finish in the order they are added to queue. But with synchronous operation it does although they may be processed by different threads. So, it behaves as this is the serial queue.
当您在 GCD 中使用同步时,任务可能会在主线程中运行。同步在给定的队列上运行一个块并等待它完成,这会导致主线程或主队列阻塞。由于主队列需要等到分派的块完成,主线程将可用于处理来自队列以外的队列的块主队列。因此在后台队列上执行的代码有可能实际上在主线程上执行。由于是并发队列,任务可能不会按照它们添加到队列的顺序完成。但是对于同步操作,尽管它们可能由不同的线程处理,但它确实如此。因此,它表现为串行队列。
Here is a summary of these experiments
以下是这些实验的总结
Remember using GCD you are only adding task to the Queue and performing task from that queue. Queue dispatches your task either in main or background thread depending on whether operation is synchronous or asynchronous. Types of queues are Serial,Concurrent,Main dispatch queue.All the task you perform is done by default from Main dispatch queue.There are already four predefined global concurrent queues for your application to use and one main queue(DispatchQueue.main).You can also manually create your own queue and perform task from that queue.
请记住,使用 GCD 您只是将任务添加到队列并从该队列执行任务。队列根据操作是同步还是异步在主线程或后台线程中调度您的任务。队列的类型有串行、并发、主调度队列。您执行的所有任务默认从主调度队列完成。已经有四个预定义的全局并发队列供您的应用程序使用,一个主队列(DispatchQueue.main)。您也可以手动创建自己的队列并从该队列执行任务。
UI Related task should always be performed from main thread by dispatching the task to Main queue.Short hand utility is DispatchQueue.main.sync/async
whereas network related/heavy operations should always be done asynchronously no matters which ever thread you are using either main or background
UI 相关任务应始终通过将任务分派到主队列从主线程执行。简短实用程序是,DispatchQueue.main.sync/async
而网络相关/繁重操作应始终异步完成,无论您使用的是主线程还是后台线程
EDIT: However, There are cases you need to perform network calls operations synchronously in a background thread without freezing UI(e.g.refreshing OAuth Token and wait if it succeed or not).You need to wrap that method inside a asynchronous operation.This way your heavy operations are executed in the order and without Blocking main thread.
编辑:但是,在某些情况下,您需要在后台线程中同步执行网络调用操作而不冻结 UI(例如刷新 OAuth 令牌并等待成功与否)。您需要将该方法包装在异步操作中。这样您的重担操作按顺序执行,不会阻塞主线程。
func doMultipleSyncTaskWithinAsynchronousOperation() {
let concurrentQueue = DispatchQueue(label: "com.queue.Concurrent", attributes: .concurrent)
concurrentQueue.async {
let concurrentQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
for i in 1...5 {
concurrentQueue.sync {
let imgURL = URL(string: "https://upload.wikimedia.org/wikipedia/commons/0/07/Huge_ball_at_Vilnius_center.jpg")!
let _ = try! Data(contentsOf: imgURL)
print("\(i) completed downloading")
}
print("\(i) executed")
}
}
}
EDIT EDIT:You can watch demo video here
编辑 编辑:您可以在此处观看演示视频
回答by bsod
First, it's important to know the difference between threads and queues and what GCD really does. When we use dispatch queues (through GCD), we're really queueing, not threading. The Dispatch framework was designed specifically to get us away from threading, as Apple admits that "implementing a correct threading solution [can] become extremely difficult, if not [sometimes] impossible to achieve." Therefore, to perform tasks concurrently (tasks that we don't want freezing the UI), all we need to do is create a queue of those tasks and hand it to GCD. And GCD handles all of the associated threading. Therefore, all we're really doing is queueing.
首先,了解线程和队列之间的区别以及 GCD 的真正作用很重要。当我们使用调度队列(通过 GCD)时,我们实际上是在排队,而不是线程。Dispatch 框架是专门为让我们远离线程而设计的,因为 Apple 承认“实施正确的线程解决方案 [可能] 变得极其困难,如果不是 [有时] 不可能实现。” 因此,要并发执行任务(我们不希望冻结 UI 的任务),我们需要做的就是创建这些任务的队列并将其交给 GCD。GCD 处理所有相关的线程。因此,我们真正要做的就是排队。
The second thing to know right away is what a task is. A task is all of the code within that queue block (not within the queue, because we can add things to a queue all of the time, but within the closure where we added it to the queue). A task is sometimes referred to as a block and a block is sometimes referred to as a task (but they are more commonly known as tasks, particularly in the Swift community). And no matter how much or little code, all of the code within the curly braces are considered a single task:
马上要知道的第二件事是什么是任务。任务是该队列块中的所有代码(不是在队列中,因为我们可以一直向队列中添加东西,而是在我们将其添加到队列的闭包中)。任务有时称为块,块有时称为任务(但它们更常称为任务,尤其是在 Swift 社区中)。并且无论代码多或少,花括号内的所有代码都被视为一项任务:
serialQueue.async {
// this is one task
// it can be any number of lines with any number of methods
}
serialQueue.async {
// this is another task added to the same queue
// this queue now has two tasks
}
And it's obvious mentioning that concurrent simply means at the same time with other things and serial means one after the other (never at the same time). To serialize something, or to put something in serial, just means to execute it from start to finish in its order from left to right, top to bottom, uninterrupted.
很明显,并发只是意味着与其他事物同时进行,串行意味着一个接一个(永远不会同时)。将某事序列化,或将某事串行化,只是意味着从头到尾按照从左到右、从上到下、不间断的顺序执行它。
There are two types of queues, serial and concurrent, but all queues are concurrent relative to each other. The fact that you want to run any code "in the background" means that you want to run it concurrently with another thread (usually the main thread). Therefore, all dispatch queues, serial or concurrent, execute their tasks concurrently relative to other queues. Any serialization performed by queues (by serial queues), have only to do with the tasks within that single [serial] dispatch queue (like in the example above where there are two tasks within the same serial queue; those tasks will be executed one after the other, never simultaneously).
有两种类型的队列,串行和并发,但所有队列都是相对于彼此并发的。您想在“后台”运行任何代码这一事实意味着您想与另一个线程(通常是主线程)同时运行它。因此,所有的调度队列,无论是串行的还是并发的,都相对于其他队列并发地执行它们的任务。由队列(由串行队列)执行的任何序列化,只与单个 [串行] 调度队列中的任务有关(如上面的示例中,同一串行队列中有两个任务;这些任务将在一个之后执行另一个,永远不会同时)。
Serial queues(often known as private dispatch queues) guarantee the execution of tasks one at a time from start to finish in the order that they were added to that specific queue. This is the only guarantee of serialization anywhere in the discussion of dispatch queues--that the specific tasks within a specific serial queue are executed in serial. Serial queues can, however, run simultaneously with other serial queues if they are separate queues because, again, all queues are concurrent relative to each other. All tasks run on distinct threads but not every task is guaranteed to run on the same thread (not important, but interesting to know). And the iOS framework does not come with any ready-to-use serial queues, you must make them. Private (non-global) queues are serial by default, so to create a serial queue:
串行队列(通常称为私有调度队列)保证从开始到结束按任务添加到特定队列的顺序一次执行一个任务。这是讨论调度队列中任何地方序列化的唯一保证--特定串行队列中的特定任务以串行方式执行。但是,如果串行队列是单独的队列,则它们可以与其他串行队列同时运行,因为同样,所有队列都是相对于彼此并发的。所有任务都在不同的线程上运行,但并非每个任务都保证在同一线程上运行(不重要,但很有趣)。而且 iOS 框架没有自带任何现成的串行队列,你必须自己制作。私有(非全局)队列默认是串行的,因此要创建串行队列:
let serialQueue = DispatchQueue(label: "serial")
You can make it concurrent through its attribute property:
您可以通过其属性属性使其并发:
let concurrentQueue = DispatchQueue(label: "concurrent", attributes: [.concurrent])
But at this point, if you aren't adding any other attributes to the private queue, Apple recommends that you just use one of their ready-to-go global queues (which are all concurrent).
但是此时,如果您不向私有队列添加任何其他属性,Apple 建议您只使用其准备就绪的全局队列之一(它们都是并发的)。
Concurrent queues(often known as global dispatch queues) can execute tasks simultaneously; the tasks are, however, guaranteed to initiatein the order that they were added to that specific queue, but unlike serial queues, the queue does not wait for the first task to finish before starting the second task. Tasks (as with serial queues) run on distinct threads and (as with serial queues) not every task is guaranteed to run on the same thread (not important, but interesting to know). And the iOS framework comes with four ready-to-use concurrent queues. You can create a concurrent queue using the above example or by using one of Apple's global queues (which is usually recommended):
并发队列(通常称为全局调度队列)可以同时执行任务;然而,这些任务保证按照它们被添加到该特定队列的顺序启动,但与串行队列不同,队列在启动第二个任务之前不会等待第一个任务完成。任务(与串行队列一样)在不同的线程上运行,并且(与串行队列一样)并非每个任务都保证在同一线程上运行(不重要,但很有趣)。并且 iOS 框架带有四个随时可用的并发队列。您可以使用上面的示例或使用 Apple 的全局队列之一(通常推荐)来创建并发队列:
let concurrentQueue = DispatchQueue.global(qos: .default)
RETAIN-CYCLE RESISTANT: Dispatch queues are reference-counted objects but you do not need to retain and release global queues because they are global, and thus retain and release is ignored. You can access global queues directly without having to assign them to a property.
RETAIN-CYCLE RESISTANT:调度队列是引用计数对象,但你不需要保留和释放全局队列,因为它们是全局的,因此保留和释放被忽略。您可以直接访问全局队列,而无需将它们分配给属性。
There are two ways to dispatch queues: synchronously and asynchronously.
调度队列有两种方式:同步和异步。
Sync dispatchingmeans that the thread where the queue was dispatched (the calling thread) pauses after dispatching the queue and waits for the task in that queue block to finish executing before resuming. To dispatch synchronously:
同步分派意味着分派队列的线程(调用线程)在分派队列后暂停,等待该队列块中的任务完成执行后再恢复。同步调度:
DispatchQueue.global(qos: .default).sync {
// task goes in here
}
Async dispatchingmeans that the calling thread continues to run after dispatching the queue and does not wait for the task in that queue block to finish executing. To dispatch asynchronously:
异步调度意味着调用线程在调度队列后继续运行,不等待该队列块中的任务完成执行。异步调度:
DispatchQueue.global(qos: .default).async {
// task goes in here
}
Now one might think that in order to execute a task in serial, a serial queue should be used, and that's not exactly right. In order to execute multipletasks in serial, a serial queue should be used, but all tasks (isolated by themselves) are executed in serial. Consider this example:
现在有人可能认为为了串行执行任务,应该使用串行队列,这并不完全正确。为了串行执行多个任务,应该使用串行队列,但所有任务(自己隔离的)都是串行执行的。考虑这个例子:
whichQueueShouldIUse.syncOrAsync {
for i in 1...10 {
print(i)
}
for i in 1...10 {
print(i + 100)
}
for i in 1...10 {
print(i + 1000)
}
}
No matter how you configure (serial or concurrent) or dispatch (sync or async) this queue, this task will always be executed in serial.The third loop will never run before the second loop and the second loop will never run before the first loop. This is true in any queue using any dispatch. It's when you introduce multiple tasks and/or queues where serial and concurrency really come into play.
无论您如何配置(串行或并发)或调度(同步或异步)此队列,此任务将始终以串行方式执行。第三个循环永远不会在第二个循环之前运行,第二个循环永远不会在第一个循环之前运行。在使用任何调度的任何队列中都是如此。当您引入多个任务和/或队列时,串行和并发才真正发挥作用。
Consider these two queues, one serial and one concurrent:
考虑这两个队列,一个是串行的,一个是并发的:
let serialQueue = DispatchQueue(label: "serial")
let concurrentQueue = DispatchQueue.global(qos: .default)
Say we dispatch two concurrent queues in async:
假设我们异步调度两个并发队列:
concurrentQueue.async {
for i in 1...5 {
print(i)
}
}
concurrentQueue.async {
for i in 1...5 {
print(i + 100)
}
}
1
101
2
102
103
3
104
4
105
5
Their output is jumbled (as expected) but notice that each queue executed its own task in serial. This is the most basic example of concurrency--two tasks running at the same time in the background in the same queue. Now let's make the first one serial:
他们的输出是混乱的(如预期的那样),但请注意,每个队列都在串行执行自己的任务。这是并发的最基本例子——两个任务在后台同时运行在同一个队列中。现在让我们将第一个序列化:
serialQueue.async {
for i in 1...5 {
print(i)
}
}
concurrentQueue.async {
for i in 1...5 {
print(i + 100)
}
}
101
1
2
102
3
103
4
104
5
105
Isn't the first queue supposed to be executed in serial? It was (and so was the second). Whatever else happened in the background is not of any concern to the queue. We told the serial queue to execute in serial and it did... but we only gave it one task. Now let's give it two tasks:
第一个队列不应该串行执行吗?它是(第二个也是)。后台发生的任何其他事情都与队列无关。我们告诉串行队列以串行方式执行,它确实做到了……但我们只给了它一项任务。现在让我们给它两个任务:
serialQueue.async {
for i in 1...5 {
print(i)
}
}
serialQueue.async {
for i in 1...5 {
print(i + 100)
}
}
1
2
3
4
5
101
102
103
104
105
And this is the most basic (and only possible) example of serialization--two tasks running in serial (one after the other) in the background (to the main thread) in the same queue. But if we made them two separate serial queues (because in the above example they are the same queue), their output is jumbled again:
这是序列化的最基本(也是唯一可能的)示例——在同一队列中的后台(到主线程)中串行(一个接一个)运行的两个任务。但是如果我们让它们成为两个单独的串行队列(因为在上面的例子中它们是同一个队列),它们的输出再次混乱:
serialQueue.async {
for i in 1...5 {
print(i)
}
}
serialQueue2.async {
for i in 1...5 {
print(i + 100)
}
}
1
101
2
102
3
103
4
104
5
105
And this is what I meant when I said all queues are concurrent relative to each other. These are two serial queues executing their tasks at the same time (because they are separate queues). A queue does not know or care about other queues. Now lets go back to two serial queues (of the same queue) and add a third queue, a concurrent one:
这就是我所说的所有队列相对于彼此并发时的意思。这是两个同时执行任务的串行队列(因为它们是独立的队列)。一个队列不知道或不关心其他队列。现在让我们回到两个串行队列(同一队列的)并添加第三个队列,一个并发队列:
serialQueue.async {
for i in 1...5 {
print(i)
}
}
serialQueue.async {
for i in 1...5 {
print(i + 100)
}
}
concurrentQueue.async {
for i in 1...5 {
print(i + 1000)
}
}
1
2
3
4
5
101
102
103
104
105
1001
1002
1003
1004
1005
That's kind of unexpected, why did the concurrent queue wait for the serial queues to finish before it executed? That's not concurrency. Your playground may show a different output but mine showed this. And it showed this because my concurrent queue's priority wasn't high enough for GCD to execute its task sooner. So if I keep everything the same but change the global queue's QoS (its quality of service, which is simply the queue's priority level) let concurrentQueue = DispatchQueue.global(qos: .userInteractive)
, then the output is as expected:
这有点出乎意料,为什么并发队列在执行之前等待串行队列完成?那不是并发。你的操场可能会显示不同的输出,但我的显示了这一点。它显示了这一点,因为我的并发队列的优先级不够高,无法让 GCD 更快地执行其任务。因此,如果我保持所有内容不变但更改全局队列的 QoS(其服务质量,即队列的优先级)let concurrentQueue = DispatchQueue.global(qos: .userInteractive)
,则输出如预期:
1
1001
1002
1003
2
1004
1005
3
4
5
101
102
103
104
105
The two serial queues executed their tasks in serial (as expected) and the concurrent queue executed its task quicker because it was given a high priority level (a high QoS, or quality of service).
两个串行队列以串行方式执行任务(如预期),并发队列执行任务更快,因为它被赋予了高优先级(高 QoS 或服务质量)。
Two concurrent queues, like in our first print example, show a jumbled printout (as expected). To get them to print neatly in serial, we would have to make both of them the same serial queue (the same instance of that queue, as well, not just the same label). Then each task is executed in serial with respect to the other. Another way, however, to get them to print in serial is to keep them both concurrent but change their dispatch method:
两个并发队列,就像我们的第一个打印示例一样,显示出混乱的打印输出(如预期)。为了让它们以串行方式整齐地打印,我们必须使它们成为同一个串行队列(该队列的同一个实例,而不仅仅是同一个标签)。然后每个任务相对于另一个依次执行。然而,让它们串行打印的另一种方法是保持它们并发但更改它们的调度方法:
concurrentQueue.sync {
for i in 1...5 {
print(i)
}
}
concurrentQueue.async {
for i in 1...5 {
print(i + 100)
}
}
1
2
3
4
5
101
102
103
104
105
Remember, sync dispatching only means that the calling thread waits until the task in the queue is completed before proceeding. The caveat here, obviously, is that the calling thread is frozen until the first task completes, which may or may not be how you want the UI to perform.
请记住,同步调度仅意味着调用线程在继续之前等待队列中的任务完成。显然,这里的警告是调用线程会被冻结,直到第一个任务完成,这可能是也可能不是您希望 UI 执行的方式。
And it is for this reason that we cannot do the following:
正是因为这个原因,我们不能做以下事情:
DispatchQueue.main.sync { ... }
This is the only possible combination of queues and dispatching methods that we cannot perform—synchronous dispatching on the main queue. And that's because we are asking the main queue to freeze until we execute the task within the curly braces... which we dispatched to the main queue, which we just froze. This is called deadlock. To see it in action in a playground:
这是我们无法执行的唯一可能的队列和调度方法的组合——主队列上的同步调度。那是因为我们要求主队列冻结,直到我们执行花括号内的任务......我们将其分派到主队列,我们只是冻结了它。这称为死锁。要在操场上看到它的运行情况:
DispatchQueue.main.sync { // stop the main queue and wait for the following to finish
print("hello world") // this will never execute on the main queue because we just stopped it
}
// deadlock
For further reading, I recommend the following:
为了进一步阅读,我推荐以下内容:
回答by Keith
If I understand correctly about how GCD works, I think there are two types of DispatchQueue
, serial
and concurrent
, at the same time, there are two way how DispatchQueue
dispatch its tasks, the assigned closure
, first one is async
, and the other is sync
. Those together determines how the closure (task) actually is executed.
如果我理解正确有关GCD是如何工作的,我觉得有两种类型的DispatchQueue
,serial
并且concurrent
,在同一时间,有两个办法如何DispatchQueue
派遣其任务,分配的closure
,第一个是async
,另一个是sync
。这些共同决定了闭包(任务)的实际执行方式。
I found that serial
and concurrent
mean how many threads that queue can use, serial
means one, whereas concurrent
means many. And sync
and async
mean the task will be executed on which thread, the caller's thread or the thread underlying that queue, sync
means run on caller's thread whereas async
means run on the underlying thread.
我发现,serial
和concurrent
平均多少线程队列可以使用,serial
手段之一,而concurrent
意味着许多。而sync
和async
平均任务将哪个线程上执行,调用者线程或线程队列底层,sync
意味着调用者的线程中运行,而async
手段底层线程上运行。
The following is experimental code that can run on Xcode playground.
以下是可以在 Xcode playground 上运行的实验代码。
PlaygroundPage.current.needsIndefiniteExecution = true
let cq = DispatchQueue(label: "concurrent.queue", attributes: .concurrent)
let cq2 = DispatchQueue(label: "concurent.queue2", attributes: .concurrent)
let sq = DispatchQueue(label: "serial.queue")
func codeFragment() {
print("code Fragment begin")
print("Task Thread:\(Thread.current.description)")
let imgURL = URL(string: "http://stackoverflow.com/questions/24058336/how-do-i-run-asynchronous-callbacks-in-playground")!
let _ = try! Data(contentsOf: imgURL)
print("code Fragment completed")
}
func serialQueueSync() { sq.sync { codeFragment() } }
func serialQueueAsync() { sq.async { codeFragment() } }
func concurrentQueueSync() { cq2.sync { codeFragment() } }
func concurrentQueueAsync() { cq2.async { codeFragment() } }
func tasksExecution() {
(1...5).forEach { (_) in
/// Using an concurrent queue to simulate concurent task executions.
cq.async {
print("Caller Thread:\(Thread.current.description)")
/// Serial Queue Async, tasks run serially, because only one thread that can be used by serial queue, the underlying thread of serial queue.
//serialQueueAsync()
/// Serial Queue Sync, tasks run serially, because only one thread that can be used by serial queue,one by one of the callers' threads.
//serialQueueSync()
/// Concurrent Queue Async, tasks run concurrently, because tasks can run on different underlying threads
//concurrentQueueAsync()
/// Concurrent Queue Sync, tasks run concurrently, because tasks can run on different callers' thread
//concurrentQueueSync()
}
}
}
tasksExecution()
Hope it can be helpful.
希望它可以有所帮助。
回答by Yunus Nedim Mehel
I like to think this using this metaphor (Here's the linkto the original image):
我喜欢用这个比喻来思考这一点(这是原始图像的链接):
Let's imagine your dad is doing the dishes and you've just had a glass of soda. You bring the glass to your dad to clean it up, putting it besides the other dish.
假设你爸爸正在洗碗,而你刚喝了一杯苏打水。你把玻璃杯拿给爸爸清理,放在另一道菜旁边。
Now your dad is doing the dishes all by himself, so he's going to have to do them one by one: Your dad here represents a serial queue.
现在你爸爸一个人洗碗,所以他将不得不一个一个地做:你爸爸在这里代表一个串行队列。
But you're not really interested in standing there and watching it get cleaned up. So, you drop the glass, and go back to your room: this is called an async dispatch. Your dad might or might not let you know once he's done but the important bit is that you're not waiting for the glass to be cleaned up; you go back to your room to do, you know, kid stuff.
但是你对站在那里看着它被清理并不真正感兴趣。所以,你放下杯子,回到你的房间:这被称为异步调度。你爸爸可能会也可能不会在他完成后让你知道,但重要的是你不是在等待玻璃被清理干净;你回到你的房间做,你知道,孩子的事情。
Now let's assume you're still thirsty and want to have some water on that same glass that happens to be your favourite, and you really want it back as soon as it's cleaned up. So, you stand there and watch your dad doing the dishes until yours is done. This is a sync dispatch, since you're blocked while you are waiting for the task to be finished.
现在让我们假设您仍然口渴,想要在碰巧是您最喜欢的同一个玻璃杯上喝一些水,并且您真的希望它在清理干净后立即恢复。所以,你站在那里看着你爸爸洗碗,直到你洗完为止。这是一个同步调度,因为您在等待任务完成时被阻塞。
And finally let's say your mom decides to help your dad and joins him doing the dishes. Now the queue becomes a concurrent queuesince they can clean multiple dishes at the same time; but note that you can still decide to wait there or go back to your room, regardless of how they work.
最后假设你妈妈决定帮助你爸爸并和他一起洗碗。现在队列变成了并发队列,因为他们可以同时清洗多个盘子;但请注意,无论它们如何工作,您仍然可以决定在那里等待或返回您的房间。
Hope this helps
希望这可以帮助
回答by CrazyPro007
1. I am reading that serial queues are created and used in order to execute tasks one after the other . However, what happens if:- ? I create a serial queue ? I use dispatch_async (on the serial queue I just created) three times to dispatch three blocks A,B,C
1. 我读到串行队列的创建和使用是为了一个接一个地执行任务。但是,如果:- 会发生什么?我创建了一个串行队列?我使用 dispatch_async(在我刚刚创建的串行队列上)三次来分派三个块 A、B、C
ANSWER:- All three blocks executed one after the another.I have created one sample code that helps to understand.
答案:- 所有三个块都一个接一个地执行。我创建了一个有助于理解的示例代码。
let serialQueue = DispatchQueue(label: "SampleSerialQueue")
//Block first
serialQueue.async {
for i in 1...10{
print("Serial - First operation",i)
}
}
//Block second
serialQueue.async {
for i in 1...10{
print("Serial - Second operation",i)
}
}
//Block Third
serialQueue.async {
for i in 1...10{
print("Serial - Third operation",i)
}
}