构造多级dispatchQueue时会发生什么不好的事情吗

时间:2019-01-09 03:06:58

标签: ios swift grand-central-dispatch

片段1

let workerQueue = DispatchQueue(label: "foo")
workderQueue.async {
   #code
   DispatchQueue.main.async {
       #code
      workerQueue.async {
            #code

      }
   }
}

摘要2

let workerQueue = DispatchQueue(label: "foo")
DispatchQueue.main.async {
   #code
   workerQueue.async {
       #code
      DispatchQueue.main.async {
            #code

      }
   }
}

编写代码段1或代码段2这样的代码可以吗?主线程会被阻塞吗?

3 个答案:

答案 0 :(得分:1)

如果您在另一个线程上,则只需要在所需线程上执行代码即可。

例如,因为默认情况下,应用程序在主线程上运行。但是,如果任务在Background线程上运行并在其内部修改UI元素,则应将其包含在主线程中。

some_background/other_thread_task {
  DispatchQueue.main.async {
       //UI Update
       self.myTableView.reloadData()
  }
}

答案 1 :(得分:1)

两个片段的结构相同。这种结构是完全正常的,并且恰好是切换线程的方法。

答案 2 :(得分:1)

不,这些模式没有内在的“坏”之处。

话虽如此,典型的模式是:

workerQueue.async {
    // do something computationally intensive

    DispatchQueue.main.async {
        // update UI and/or model
    }
}

这是由于“我的工作量足够大,我不希望它在主队列上运行(并且会对UX产生不利影响),但是完成后,我需要更新UI和/或模型。”

非常罕见的是,您还具有更高级别的嵌套,即“当我完成UI的更新后,我需要将其他内容分派回我的同一工作队列”,如您的第一个示例所示。如果您有一个实际的例子,也许可以与我们分享,因为可能会有更优雅的重构方法来避免“塔式”嵌套调度,这可能会使代码难以呈现。

在第二个示例中,您首先要分派到主线程也是有点不典型。 (是的,我们偶尔需要这样做,但这并不常见。)我猜您是在假设您已经在其他某个线程上了(尽管并非总是如此)。如果是这种情况,则代码期望您位于特定线程上,那么您可能需要明确表示该假设,例如:

func foo() {
    dispatchPrecondition(condition: .onQueue(workerQueue))

    // do some stuff that should be on the `workerQueue`

    DispatchQueue.main.async {
        // update UI, etc.

        ...
    }
}

最重要的是,任意级别的嵌套绝对没有问题,特别是如果使用async进行嵌套...如果不注意,使用sync进行嵌套会引发死锁问题。但是,从实用的“将来的程序员可以阅读此代码并清楚地推断出最终结果是什么”开始,您通常会希望对此加以限制,以免引起混淆。


作为一个实际示例,我可能正在某些专用功能队列(例如数据库访问队列或图像处理队列)上做某事,并且可能需要使用另一个同步队列来确保线程安全的访问,并且可能需要在主队列上进行UI更新。但是我通常没有三层队列的塔,但是,例如,我封装了这些不同的层以避免混淆(例如,我有一个读写器泛型,它封装了我用于线程的并发队列的详细信息,安全访问),并且我的代码避免了在任何给定方法中复杂地混合不同类型的调度队列。在任何特定的抽象级别上,我都试图同时避免太多不同的队列类型。