我有一个进程可以运行很长时间,并且我希望能够中断。
func longProcess (shouldAbort: @escaping ()->Bool) {
// Runs a long loop and periodically checks shouldAbort(),
// returning early if shouldAbort() returns true
}
这里是使用它的班级:
class Example {
private var abortFlag: NSObject? = .init()
private var dispatchQueue: DispatchQueue = .init(label: "Example")
func startProcess () {
let shouldAbort: ()->Bool = { [weak abortFlag] in
return abortFlag == nil
}
dispatchQueue.async {
longProcess(shouldAbort: shouldAbort)
}
}
func abortProcess () {
self.abortFlag = nil
}
}
shouldAbort
闭包捕获对weak
的{{1}}引用,并检查该引用是指向abortFlag
还是指向nil
。由于引用为NSObject
,因此如果原始weak
被释放,则闭包捕获的引用将突然为NSObject
,并且闭包将开始返回nil
。将在true
函数期间重复调用该闭包,这在私有longProcess
上发生。 dispatchQueue
类上的abortProcess
方法将从其他队列中外部调用。如果有人在Example
试图执行检查以查看abortProcess()
是否已经被释放的同时,打电话给abortFlag
,从而释放longProcess
,该怎么办?检查abortFlag
是否是线程安全的操作?
答案 0 :(得分:2)
您可以将调度的任务创建为DispatchWorkItem
,该任务已经具有线程安全的isCancelled
属性。然后,您可以将DispatchWorkItem
分派到队列中,并定期检查其isCancelled
。然后,您只需cancel
派发点就可以停止它。
或者,当尝试将某些工作包装到对象中时,我们通常使用Operation
,它将任务很好地封装在自己的类中:
class SomeLongOperation: Operation {
override func main() {
// Runs a long loop and periodically checks `isCancelled`
while !isCancelled {
Thread.sleep(forTimeInterval: 0.1)
print("tick")
}
}
}
然后创建队列并将操作添加到该队列:
let queue = OperationQueue()
let operation = SomeLongOperation()
queue.addOperation(operation)
并取消操作:
operation.cancel()
或
queue.cancelAllOperations()
最重要的是,无论您使用Operation
(坦率地说,是将某些任务包装在其自己的对象中的“首选”解决方案)还是使用DispatchWorkItem
自己动手,想法是相同的,即您不需要拥有自己的state属性来检测任务的取消。调度队列和操作队列都已经具有不错的机制来为您简化此过程。
答案 1 :(得分:1)
我看到此错误(Weak properties are not thread safe when reading SR-192)表示弱引用读取不是线程安全的,但已修复,这表明(在运行时中不存在任何错误)弱引用读取旨在线程安全。
也很有趣:Friday Q&A 2017-09-22: Swift 4 Weak References by Mike Ash