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


99

我已经使用DispatchQueue.main.async了很长时间来执行与UI相关的操作。



Swift同时提供DispatchQueue.main.asyncDispatchQueue.main.sync,并且两者都在主队列上执行。



谁能告诉我他们之间的区别?我什么时候应该使用每个?



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

}

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

Answers:


45

使用async它时,可以使调用队列继续前进,而不必等到执行分派的块。相反,sync这将使调用队列停止并等待,直到您在块中分派的工作完成为止。因此sync容易导致死锁。尝试DispatchQueue.main.sync从主队列中运行,该应用将冻结,因为调用队列将等待,直到分派的块结束,但它甚至无法启动(因为队列已停止并等待)

什么时候使用sync?当您需要等待在不同队列上完成的事情然后才继续在当前队列上工作时

使用同步的示例:

在串行队列上,可以将其sync用作互斥体,以确保只有一个线程能够同时执行受保护的代码段。


DispatchQueue.main.sync从后台线程调用会是错误的吗?
蜂蜜

@Honey通常不,这样的调用没有任何问题(只要主队列不会做任何繁重且耗时的事情),但是实际上我无法想到您真正需要此调用的情况。肯定应该有一个更好的解决方案
Andrey Chernukha,

1
@Honey一种这样的情况是从PhotoKit API更新PHAssets的CollectionView,如此处的文档所示:developer.apple.com/documentation/photokit/…–
teacup

1
@teacup有趣。我只是想知道如果我们打电话到async那里会有什么不同?我的意思是,由于线程之后没有其他内容,因此没有任何区别。如果是这样的DispatchQueue.main.sync {block1}; DispatchQueue.main.sync {block2};话,那将是有道理的。但是,当没有其他障碍时,我想不出使用DispatchQueue.main.sync {Oneblock}over 的好处DispatchQueue.main.async {Oneblock}。对于他们两个,他们都将获得mainQueue优先级/即时性,并且没有任何中断。
亲爱的

3
当您在主线程上时,@ Honey“因为线程之后没有其他东西了”,这是不正确的,它负责处理与应用程序的所有用户交互。因此,例如,用户可能会在photoLibraryDidChange返回带有更新的数据源之前删除另一张照片,从而导致致命的不一致错误。
茶杯

160

为什么要并发?

一旦您向应用程序添加了繁重的任务(例如数据加载),它就会减慢您的UI工作甚至冻结它。并发使您可以“同时”执行2个或更多任务。这种方法的缺点是线程安全性并不总是那么容易控制。例如,当不同的任务想要访问相同的资源时,例如尝试在不同的线程上更改相同的变量或访问已经被不同的线程阻塞的资源。

我们需要注意一些抽象。

  • 队列。
  • 同步/异步任务性能。
  • 优先事项。
  • 常见的麻烦。

Queue列

必须是串行并发的。以及全球私有

使用串行队列,任务将一个接一个地完成,而使用并发队列,任务将同时执行,并且将按意外的时间表完成。与并发队列相比,同一组任务将在串行队列上花费更多的时间。

您可以创建自己的专用队列串行并发队列)或使用已经可用的全局(系统)队列。该主队列是唯一的串行队列所有的出全球队列

强烈建议不要在主队列上执行不与UI工作相关的繁重任务(从网络中加载数据),而是在其他队列上执行这些任务,以保持UI冻结并响应用户操作。如果我们允许在其他队列上更改UI,则可以按不同的意外时间表和速度进行更改。某些UI元素可以在需要之前或之后绘制。它可能会使UI崩溃。我们还需要记住,由于全局队列系统队列,因此系统可以在它们上运行其他一些任务。

服务质量/优先级

队列也有不同的QoS(服务质量),其中规定了任务执行优先级(这里从最高到最低):
.userInteractive - 主队列
.userInitiated -用户启动的任务哪个用户等待响应的一些
.utility -为任务这需要一些时间并且不需要立即响应,例如使用
data.background-用于与可视部分无关的任务以及对完成时间不严格的任务。

还有一个

.default队列,它不传输qos信息。如果这是不可能检测到的QoSqos将在.userInitiated.utility之间使用。

任务可以同步异步执行。

  • 同步功能仅在任务完成后才将控制权返回到当前队列。它阻止队列并等待直到任务完成。

  • 异步功能在发送任务以在其他队列上执行之后立即将控制权返回到当前队列。它不会等到任务完成。它不会阻塞队列。

常见问题。

程序员在开发并发应用程序时犯的最普遍的错误如下:

  • 竞争状态 -在应用程序工作取决于代码部分执行的顺序时引起。
  • 优先级倒置 -当较高优先级的任务由于某些资源被阻塞而等待较小优先级的任务完成时
  • 死锁 -当一些队列无限等待源(变量,数据等)已被其中一些队列阻塞时。

永远不要在主队列上调用同步功能
如果您在主队列上调用同步功能,它将阻塞队列,并且队列将等待任务完成,但是由于队列已停止,任务甚至无法启动,因此任务将永远不会完成。已经被封锁。这称为死锁

什么时候使用同步? 当我们需要等到任务完成时。当我们确保某些函数/方法没有被双重调用时,Fe。如果我们有同步,并试图防止它被重复调用,直到完全完成。以下是针对此问题的一些代码:
如何找出导致iOS设备上的错误崩溃报告的原因?


3
我认为“永远不要在主队列上调用同步功能”是正确的。在某些情况下,您可能会在主线程中调用同步,例如,当您有一个全局计数器需要每个对象使用并增加以下值时:dispatchQueue.sync {count + = 1; self.orderId =计数}
以利沙太极扣

6
QOS类-.userInteractive不是主队列。
Kunal Shah

1
DispatchQueue.main.sync从后台线程调用会是错误的吗?
亲爱的

1
@Honey,调用它没有错,但是根据我的经验,您会发现自己调用了DispatchQueue.main.async而不是sync。
James Kim

2
说永远不要在当前队列上调用sync()函数会更准确吗?如果我正确理解的话,如果您在另一个队列中,则在主队列上调用sync()并没有错。
ykay

0

syncasync方法对调用它们的队列没有影响。

sync将阻止该线程调用它,而不是队列调用它。它的属性DispatchQueue决定了DispatchQueue将等待任务执行(串行队列)还是可以在当前任务完成之前运行下一个任务(并发队列)。

因此,即使DispatchQueue.main.async是异步调用,由于在主线程上顺序执行其操作,因此添加在其中的繁重操作也会冻结UI。如果从后台线程调用此方法,则即使UI似乎被冻结,控件也会立即返回该线程。这是因为asyncDispatchQueue.main


0

GCD允许您执行任务synchronouslyasynchronously[关于] [更多]

synchronous(阻止并等待)函数将在任务完成时返回控件

asynchronous(分派并继续)功能立即返回控件,将任务分派以开始到适当的队列,但不等待其完成。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.