如果从同一个线程(线程1主线程)调用uploadFailed(for id: String)
,uploadSuccess()
和updateOnStart(_ id: String)
,我知道我们不需要同步队列。如果每次上载都从不同的线程调用函数,该怎么办。在哪里确保同步?它是上传状态还是状态,还是状态?
enum FlowState {
case started(uploads: [String])
case submitted
case failed
}
class Session {
var state: FlowState
let syncQueue: DispatchQueue = .init(label: "Image Upload Sync Queue",
qos: .userInitiated,
attributes: [],
autoreleaseFrequency: .workItem)
init(state: FlowState) {
self.state = state
}
mutating func updateOnStart(_ id: String) {
guard case .started(var uploads) = state else {
return
}
uploads.append(id)
state = .started(uploads)
}
mutating func uploadFailed(for id: String) {
guard case .started(var uploads) = state else {
return
}
uploads.removeAll { $0 == id }
if uploads.isEmpty {
state = .failed
} else {
state = .started(uploads)
}
}
mutating func uploadSuccess() {
state = .submitted
}
}
我们是否同时同步uploads
数组操作和状态,如下所示?
syncQueue.sync {
uploads.append(id)
state = .started(uploads)
}
syncQueue.sync {
uploads.removeAll { $0 == id }
if uploads.isEmpty {
state = .failed
} else {
state = .started(uploads)
}
}
OR
syncQueue.sync {
state = .started(uploads)
}
syncQueue.sync {
if uploads.isEmpty {
state = .failed
} else {
state = .started(uploads)
}
}
网络呼叫的完成处理程序可以更新Session
的state
属性。例如,用户选择10张图像并上传。完成后可能是失败或成功。对于我们上传的每个图像,我们缓存资源id
,如果上传失败,则将其删除。当所有图像上传失败时,我们将更新状态.failed
。我们只关心上传一张图片。上传一张图片后,我们会将状态更新为.submitted
使用同步来确保与FlowState
的线程安全交互是完全有效的。
一些观察:
您提出了两种选择:
syncQueue.sync {
uploads.append(id)
self.state = .started(uploads)
}
或
syncQueue.sync {
state = .started(uploads)
}
这两个都不对。如果线程1和线程2同时调用此例程该怎么办。考虑:
uploads
uploads
;sync
块,并将记录追加到其本地副本,保存它,并留下其sync
闭包;sync
块,并将其他记录追加到其自己的本地副本(没有线程1添加的记录),将其保存,并保留其sync
闭包。您将丢失附加的线程1。 因此,您需要采用第三种更广泛的同步机制,将检索,附加和存储uploads
的整个过程包装在同步机制中:
syncQueue.sync {
guard case .started(var uploads) = self.state else {
return
}
uploads.append(id)
state = .started(uploads)
}
您没有显示state
的任何其他读数,但是如果有,请确保也同步您的读数。与state
的所有交互都必须同步。
一个细微的问题:如果您要同步对state
的访问,请确保将其设为private
,以便没有外部代码可以访问它(否则可能会阻碍您尝试确保线程安全的交互)。您需要在同步机制中包装所有读取和写入。
您可能也应该将同步队列设为私有,因为也无需公开该队列。