我如何确保DispatchQueue专门在主线程上执行一些代码?

问题描述 投票:-1回答:1

我有一个管理数组的单例。可以从多个线程访问此单例,因此它具有自己的内部DispatchQueue来管理跨线程的读/写访问。为简单起见,我们将其称为串行队列。

有时会从数组中读取单例并更新UI。我该如何处理?

我的内部调度队列不知道哪个线程,对吗?这只是我无需担心的实现细节?在大多数情况下,这似乎很好,但是在这一特定功能中,我需要确保它使用了主线程。

可以按照以下方式做一些事情吗:

myDispatchQueue.sync { // Synchronize with internal queue to ensure no writes/reads happen at the same time
    DispatchQueue.main.async { // Ensure that it's executed on the main thread
        for item in internalArray {
            // Pretend internalArray is an array of strings
            someLabel.text = item
        }
    }
}

所以我的问题是:

  1. 可以吗?嵌套调度队列似乎很奇怪/错误。有没有更好的办法?也许像myDispatchQueue.sync(forceMainThread: true) { ... }
  2. 如果我不使用DispatchQueue.main.async { ... },并且从主线程调用了该函数,是否可以确定我的内部调度队列将在与调用它相同的(主)线程上执行该函数?还是这可能是“实现细节”,但也可以在后台线程上调用它?

基本上,我感到困惑的是,线程看起来像是您不应该为队列担心的实现细节,但是当您确实需要担心时,在偶然的机会上会发生什么呢?

简单的示例代码:

class LabelUpdater {
    static let shared = LabelUpdater()

    var strings: [String] = []
    private let dispatchQueue: dispatchQueue

    private init {
        dispatchQueue = DispatchQueue(label: "com.sample.me.LabelUpdaterQueue")
        super.init()
    }

    func add(string: String) {
        dispatchQueue.sync {
            strings.append(string)
        }
    }

    // Assume for sake of example that `labels` is always same array length as `strings`
    func updateLabels(_ labels: [UILabel]) {
        // Execute in the queue so that no read/write can occur at the same time.
        dispatchQueue.sync {
            // How do I know this will be on the main thread? Can I ensure it?
            for (index, label) in labels.enumerated() {
                label.text = strings[index]
            }
        }
    }
}
ios swift multithreading cocoa-touch grand-central-dispatch
1个回答
0
投票

是的,您可以嵌套您的调度队列。我们经常这样做。

但是要非常小心。仅使用来自同步队列的分派将异步分派包装到主队列中是不够的。您的第一个示例不是线程安全的。您正在从主线程访问的数组可能正在从同步队列中更改:

enter image description here

这是race condition,但这说明了问题。因此,请确保您只是向另一个线程(在本示例中为主线程)提供要在UI中显示的任何内容的副本,而不是直接将调度分配到主队列中。


注意,所有这些都与单身无关。但是,既然您提出了这个话题,建议不要使用单例模型数据。它适用于接收器,无状态控制器等,但通常不建议使用模型数据。

我绝对不鼓励直接从单例启动UI控件更新的做法。我倾向于提供这些方法的完成处理程序闭包,并让调用者来处理由此产生的UI更新。当然,如果您想将闭包分派到主队列中(为方便起见,在许多第三方API中很常见),那就很好了。但是单身人士不应该介入并自行更新UI控件。

我假设您所做的所有这些只是出于说明的目的,但我向可能不理解这些问题的将来的读者添加了此警告。

© www.soinside.com 2019 - 2024. All rights reserved.