Swift-检查弱变量是否为nil还是线程安全的?

时间:2019-07-18 15:47:24

标签: swift queue thread-safety weak-references abort

我有一个进程可以运行很长时间,并且我希望能够中断。

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是否是线程安全的操作?

2 个答案:

答案 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