Swift3 GCD中的main.async vs main.sync()vs global().async

Shi*_*ava 7 multithreading asynchronous ios swift swift3

示例A: - 这会导致App Crash.

DispatchQueue.main.async {           
        let url = URL(string: imageUrl)
        do {
             let data = try Data(contentsOf: url!)
                DispatchQueue.main.sync {
                    self.imageIcon.image = UIImage(data: data)
                }
            }
Run Code Online (Sandbox Code Playgroud)

例B: - 但事实并非如此

DispatchQueue.global().async {  
        let url = URL(string: imageUrl)
        do {
            let data = try Data(contentsOf: url!)
                DispatchQueue.main.sync {
                    self.imageIcon.image = UIImage(data: data)
                }
            }
Run Code Online (Sandbox Code Playgroud)

据我所知,

  • x.sync意味着在主线程/ UI线程中执行操作而x.async意味着在后台线程中执行操作.
  • 全局意味着执行具有并发队列的东西,即并行任务

Quest1: - 那么为什么我的应用程序在后台线程(即main.async)执行任务时崩溃,而不是调用主线程来更新UI.

Quest2: - main.async和global().async有什么区别.

yoA*_*ex5 11

大中华区

Thread -> GCD -> Operation + OperationQueue(life cycle, dependencies between different queues, cancel)
Run Code Online (Sandbox Code Playgroud)

Grand Central Dispatch GCD libdispatchDispatchQueue以 FIFO 顺序对调度队列进行操作

DispatchQueue.<queue>.<sync/async>意味着<sync/async><queue>

队列

通常,当我们谈论时,concurrent我们谈论的是队列。线程数取决于操作系统条件。没有run loop工作线程。

concurrent具有不同优先级的队列组(主线程、高、默认、低、后台),您使用qos QoSClass(服务质量)(priority GlobalQueuePriority已弃用)在任务中传递这些队列:

  • userInteractive- 主线程 - 最优先。它可用于非常快速的计算,并立即反映在 UI 上。例如动画计算
  • userInitiated- 高优先级队列 - 与 UI 相关。长达数秒。例如加载用于在 UI 上显示的数据
  • default - 默认优先级队列
  • utility - 低优先级队列 - 最多几分钟,例如处理图像等大数据,处理...
  • background - 后台优先队列 - 应用程序在后台运行时长达几个小时,如同步数据。

GCD 支持:

  • global queue - 在整个 iOS 操作系统之间共享
  • private queue

main-全局队列,用于处理 UI的主线程上的串行队列

DispatchQueue.main
Run Code Online (Sandbox Code Playgroud)

global()-全局队列并发队列

DispatchQueue.global()
DispatchQueue.global(qos: .background)
Run Code Online (Sandbox Code Playgroud)

自定义队列: -私人队列serialconcurrent自定义队列

DispatchQueue(label: "serialQueue") // without attributes
DispatchQueue(label: "concurrentQueue", attributes: .concurrent)        
Run Code Online (Sandbox Code Playgroud)

同步/异步

sync - 阻塞当前线程并等待它在指定队列上完成

async - 不要阻塞当前线程并将执行代码块发送到指定的队列

常见错误:死锁

如果您DispatchQueue.main.syncmain线程上调用- 应用程序将被冻结,因为DispatchQueue.main.sync当分派块完成时调用立即开始等待(分派块未启动)

一些注意事项:

  • DispatchWorkItem- 延迟/取消/优先处理内部QueueDispatchGroup
  • DispatchGroup如果您打算使用单个回调执行多个异步任务,即使在不同的队列上。所有这些任务都应该分组。DispatchGroup包含线程安全计数器,当它等于 0 时notify被调用
//create group
let group = DispatchGroup()

//case 1
DispatchQueue.<queue>.async(group: group) //

//case 2 - manual
group.enter() //<- +1
DispatchQueue.global().async { 
    //logic
    group.leave() //<- -1
}

//notification
group.notify(queue: <callback_queue>) {
    //logic             
}
Run Code Online (Sandbox Code Playgroud)
  • Barrier内标志并发队列同步/异步任务担保不存在race condition(多个线程同时进行写操作)。最好的地方是自定义队列,因为它不会阻塞任何其他全局任务:
customQueue.async(flags: .barrier) { 
    //logic
    someProperty = someValue
}
Run Code Online (Sandbox Code Playgroud)
  1. 所有开始的任务都完成了
  2. 单一障碍任务
  3. 执行队列中的所有其他任务

thread safe操作可以通过Barrier在共享变量的并发队列中达到:

  • 读取 - 并发队列上的同步操作
  • 写 - 异步操作 barrier

【线程安全单例】
【Sync vs Async】
【iOS同步】


Shi*_*ava 10

简单来说,我得出的结论是 -

  • 队列 - 有3种类型的队列,即1个主队列,4个全局队列和任意数量的自定义队列.
  • 线程 - 一个是主线程和系统提供给我们的其他后台线程.

DispatchQueue.main.async

- 这意味着使用后台线程(没有阻止UI)在主队列中执行任务,当任务完成时自动更新到UI,因为它已经在主队列中.

DispatchQueue.global().async以及global().sync

它意味着使用后台线程在全局队列中执行任务,当任务完成时,比global().sync use将工作从globalQueue带到mainQueue,后者更新到UI.

我的应用程序崩溃的原因

我试图通过使用(main.sync)将完成的任务带到MainQueue,但它已经在MainQueue上,因为我没有切换Queue,这创建了DeadLock(MainQueue等待自己),导致我的应用程序崩溃


Mil*_*sáľ 6

在第一种情况下,您运行代码main然后main.sync在主线程上使用.本质上,你试图告诉main队列等待自己 - 这显然是无稽之谈,因此它会导致崩溃.

在第二种情况下,您在后台线程上运行代码,然后使用main.sync等待main线程可以运行提供的块main.sync.

一般来说,我会使用async而不是sync所有的时间,除非sync是必要的 - 并且始终将一个线程(DispatchQueue)与另一个线程同步,从不使用相同的线程.

  • 是的,“sync”只是意味着在这里等待,直到该块执行完毕,“async”不会等待。在主线程上使用“sync”时必须小心,否则它会阻止你的 UI 更新 (2认同)