我有一个管理数组的单例。可以从多个线程访问此单例,因此它具有自己的内部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
}
}
}
所以我的问题是:
myDispatchQueue.sync(forceMainThread: true) { ... }
?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]
}
}
}
}
是的,您可以嵌套您的调度队列。我们经常这样做。
但是要非常小心。仅使用来自同步队列的分派将异步分派包装到主队列中是不够的。您的第一个示例不是线程安全的。您正在从主线程访问的数组可能正在从同步队列中更改:
这是race condition,但这说明了问题。因此,请确保您只是向另一个线程(在本示例中为主线程)提供要在UI中显示的任何内容的副本,而不是直接将调度分配到主队列中。
注意,所有这些都与单身无关。但是,既然您提出了这个话题,建议不要使用单例模型数据。它适用于接收器,无状态控制器等,但通常不建议使用模型数据。
我绝对不鼓励直接从单例启动UI控件更新的做法。我倾向于提供这些方法的完成处理程序闭包,并让调用者来处理由此产生的UI更新。当然,如果您想将闭包分派到主队列中(为方便起见,在许多第三方API中很常见),那就很好了。但是单身人士不应该介入并自行更新UI控件。
我假设您所做的所有这些只是出于说明的目的,但我向可能不理解这些问题的将来的读者添加了此警告。