简体   繁体   English

DispatchQueue.main.async 和 DispatchQueue.main.sync 的区别

[英]Difference between DispatchQueue.main.async and DispatchQueue.main.sync

I have been using DispatchQueue.main.async for a long time to perform UI related operations.我一直在使用DispatchQueue.main.async进行 UI 相关操作。

Swift provides both DispatchQueue.main.async and DispatchQueue.main.sync , and both are performed on the main queue. Swift 提供了DispatchQueue.main.asyncDispatchQueue.main.sync ,并且都在主队列上执行。

Can anyone tell me the difference between them?谁能告诉我它们之间的区别? When should I use each?我应该什么时候使用?

DispatchQueue.main.async {
    self.imageView.image = imageView
    self.lbltitle.text = ""

}

DispatchQueue.main.sync {
    self.imageView.image = imageView
    self.lbltitle.text = ""
}

Why Concurrency?为什么要并发?

As soon as you add heavy tasks to your app like data loading it slows your UI work down or even freezes it.一旦您向应用程序添加繁重的任务(例如数据加载),它就会减慢您的 UI 工作速度,甚至冻结它。 Concurrency lets you perform 2 or more tasks “simultaneously”.并发使您可以“同时”执行 2 个或更多任务。 The disadvantage of this approach is that thread safety which is not always as easy to control.这种方法的缺点是线程安全性并不总是那么容易控制。 Fe when different tasks want to access the same resources like trying to change the same variable on a different threads or accessing the resources already blocked by the different threads. Fe 当不同的任务想要访问相同的资源时,例如尝试更改不同线程上的相同变量或访问已被不同线程阻塞的资源。

There are a few abstractions we need to be aware of.我们需要注意一些抽象。

  • Queues.队列。
  • Synchronous/Asynchronous task performance.同步/异步任务性能。
  • Priorities.优先事项。
  • Common troubles.常见的烦恼。

Queues队列

Must be serial or concurrent .必须是串行的并发的 As well as global or private at the same time.以及同时全局私有

With serial queues, tasks will be finished one by one while with concurrent queues, tasks will be performed simultaneously and will be finished on unexpected schedules.在串行队列中,任务将一个一个地完成,而在并发队列中,任务将同时执行并按意外的时间表完成。 The same group of tasks will take the way more time on a serial queue compared to a concurrent queue.与并发队列相比,同一组任务在串行队列上花费的时间更多。

You can create your own private queues (both serial or concurrent ) or use already available global (system) queues .您可以创建自己的专用队列串行并发)或使用已经可用的全局(系统)队列 The main queue is the only serial queue out of all of the global queues .主队列是所有全局队列中唯一的串行队列

It is highly recommended to not perform heavy tasks which are not referred to UI work on the main queue (fe loading data from the network), but instead to do them on the other queues to keep the UI unfrozen and responsive to the user actions.强烈建议不要在主队列上执行与 UI 工作无关的繁重任务(从网络加载数据),而是在其他队列上执行这些任务,以保持 UI 不冻结并响应用户操作。 If we let the UI be changed on the other queues, the changes can be made on a different and unexpected schedule and speed.如果我们让其他队列上的 UI 更改,则可以以不同且意外的时间表和速度进行更改。 Some UI elements can be drawn before or after they are needed.一些 UI 元素可以在需要之前或之后绘制。 It can crash the UI.它可能会导致用户界面崩溃。 We also need to keep in mind that since the global queues are system queues there are some other tasks can run by the system on them.我们还需要记住,由于全局队列系统队列,因此系统可以在它们上运行一些其他任务。

Quality of Service / Priority服务质量/优先级

Queues also have different qos (Quality of Service) which sets the task performing priority (from highest to lowest here):队列也有不同的qos(服务质量) ,它设置任务执行优先级(从高到低):
.userInteractive - main queue .userInteractive -主队列
.userInitiated - for the user initiated tasks on which user waits for some response .userInitiated - 用于用户启动的任务,用户等待某些响应
.utility - for the tasks which takes some time and doesn't require immediate response, eg working with data .utility - 用于需要一些时间且不需要立即响应的任务,例如处理数据
.background - for the tasks which aren't related with the visual part and which aren't strict for the completion time). .background - 用于与视觉部分无关且对完成时间不严格的任务)。

There is also还有

.default queue which does't transfer the qos information. .default队列,不传输qos信息。 If it wasn't possible to detect the qos the qos will be used between .userInitiated and .utility .如果无法检测到qos ,则将在.userInitiated.utility之间使用qos

Tasks can be performed synchronously or asynchronously .任务可以同步异步执行。

  • Synchronous function returns control to the current queue only after the task is finished.同步函数只有在任务完成后才将控制权返回到当前队列。 It blocks the queue and waits until the task is finished.它阻塞队列并等待任务完成。

  • Asynchronous function returns control to the current queue right after task has been sent to be performed on the different queue.异步函数在发送任务后立即将控制权返回到当前队列以在不同队列上执行。 It doesn't wait until the task is finished.它不会等到任务完成。 It doesn't block the queue.它不会阻塞队列。

Common Troubles.常见问题。

The most popular mistakes programmers make while projecting the concurrent apps are the following:程序员在预测并发应用程序时最常犯的错误如下:

  • Race condition - caused when the app work depends on the order of the code parts execution.竞争条件- 当应用程序工作取决于代码部分执行的顺序时引起。
  • Priority inversion - when the higher priority tasks wait for the smaller priority tasks to be finished due to some resources being blocked优先级反转- 当较高优先级的任务由于某些资源被阻塞而等待较小优先级的任务完成时
  • Deadlock - when a few queues have infinite wait for the sources (variables, data etc.) already blocked by some of these queues.死锁- 当一些队列无限等待已经被其中一些队列阻塞的源(变量、数据等)时。

NEVER call the sync function on the main queue .永远不要在主队列上调用同步函数
If you call the sync function on the main queue it will block the queue as well as the queue will be waiting for the task to be completed but the task will never be finished since it will not be even able to start due to the queue is already blocked.如果您在主队列上调用同步函数,它将阻塞队列,队列将等待任务完成但任务永远不会完成,因为它甚至无法启动,因为队列是已经屏蔽了。 It is called deadlock .它被称为死锁

When to use sync?什么时候使用同步? When we need to wait until the task is finished.当我们需要等到任务完成时。 Fe when we are making sure that some function/method is not double called. Fe,当我们确保某些函数/方法不被重复调用时。 Fe we have synchronization and trying to prevent it to be double called until it's completely finished. Fe 我们有同步并试图防止它被双重调用,直到它完全完成。 Here's some code for this concern:这是针对此问题的一些代码:
How to find out what caused error crash report on IOS device? 如何找出是什么导致了 IOS 设备上的错误崩溃报告?

When you use async it lets the calling queue move on without waiting until the dispatched block is executed.当您使用async它可以让调用队列继续移动,而无需等到执行分派的块。 On the contrary sync will make the calling queue stop and wait until the work you've dispatched in the block is done.相反, sync将使调用队列停止并等待您在块中分派的工作完成。 Therefore sync is subject to lead to deadlocks.因此sync容易导致死锁。 Try running DispatchQueue.main.sync from the main queue and the app will freeze because the calling queue will wait until the dispatched block is over but it won't be even able to start (because the queue is stopped and waiting)尝试从主队列运行DispatchQueue.main.sync并且应用程序将冻结,因为调用队列将等到调度块结束但它甚至无法启动(因为队列已停止并等待)

When to use sync ?什么时候使用sync When you need to wait for something done on a DIFFERENT queue and only then continue working on your current queue当您需要等待在 DIFFERENT 队列上完成某事,然后才继续处理当前队列时

Example of using sync:使用同步的示例:

On a serial queue you could use sync as a mutex in order to make sure that only one thread is able to perform the protected piece of code at the same time.在串行队列上,您可以将sync用作互斥锁,以确保只有一个线程能够同时执行受保护的代码段。

GCD allows you to execute a task synchronously or asynchronously [About] GCD允许您synchronouslyasynchronously执行任务[关于]

synchronous (block and wait) function returns a control when the task will be completed synchronous (阻塞和等待)函数在任务完成时返回控制

asynchronous (dispatch and proceed) function returns a control immediately, dispatching the task to start to an appropriate queue but not waiting for it to complete. asynchronous (分派和继续)函数立即返回一个控制,分派任务以开始到适当的队列但不等待它完成。

[DispatchQueue] [调度队列]

sync or async methods have no effect on the queue on which they are called. syncasync方法对调用它们的队列没有影响。

sync will block the thread from which it is called and not the queue on which it is called. sync将阻止其中它被称为并且它们不是它被称为队列中的线程。 It is the property of DispatchQueue which decides whether the DispatchQueue will wait for the task execution (serial queue) or can run the next task before current task gets finished (concurrent queue). DispatchQueue的属性决定了DispatchQueue等待任务执行(串行队列)还是可以在当前任务完成之前运行下一个任务(并发队列)。

So even when DispatchQueue.main.async is an async call, a heavy duty operation added in it can freeze the UI as its operations are serially executed on the main thread.因此,即使DispatchQueue.main.async是异步调用,添加到其中的重型操作也会冻结 UI,因为其操作是在主线程上串行执行的。 If this method is called from the background thread, control will return to that thread instantaneously even when UI seems to be frozen.如果从后台线程调用此方法,即使 UI 似乎被冻结,控制权也会立即返回该线程。 This is because async call is made on DispatchQueue.main这是因为async调用是在DispatchQueue.main

 // Main Thread
 DispatchQueue.global(qos: .userInteractive).sync {
            self.tableView.reloadData()
   }

 //Thread 4 Queue : com.apple.root.user-interactive-qos (concurrent)
 DispatchQueue.global(qos: .userInteractive).async {
            // Can't do UI Stuff here
            self.tableView.reloadData()
   }
  • Using .userInteractive for qos makes you have two options about the thread, Either you get the main thread or get a background thread. 对qos使用.userInteractive可以使您对线程有两个选择,要么获取主线程,要么获取后台线程。 It depends on how you are going to perform thread synchronously or asynchronously. 这取决于您将如何同步或异步执行线程。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM