线程安全为一个单身的吸气和二传手

我在Swift 3中创build了一个简单的单例:

class MySingleton { private var myName: String private init() {} static let shared = MySingleton() func setName(_ name: String) { myName = name } func getName() -> String { return myName } } 

由于我做了私人的init() ,并且声明shared实例是static let ,所以我认为初始化程序是线程安全的。 但是, myName的getter和setter函数呢,它们是线程安全的吗?

你写的那些getter是不是线程安全的。 在Swift中,目前最简单(最安全)的方法是使用Grand Central Dispatch队列作为locking机制。 最简单的(也是最容易推理的)方法是使用基本的串行队列。

 class MySingleton { static let shared = MySingleton() // Serial dispatch queue private let lockQueue = DispatchQueue(label: "MySingleton.lockQueue") private var _myName: String var myName: String { get { var result: String! lockQueue.sync { result = self._myName } return result } set { lockQueue.sync { self._myName = newValue } } } private init() { _myName = "initial name" } } 

使用串行调度队列将保证先进先出执行以及实现对数据的“locking”。 也就是说,数据在更改时无法读取。 在这种方法中,我们使用sync来执行数据的实际读写,这意味着调用者将总是被迫等待其轮到,类似于其他locking原语。

注意:这不是最高性能的方法,但它很容易阅读和理解。 避免竞态条件是一个很好的通用的解决scheme,但并不意味着为并行algorithm开发提供同步。

来源: https : //mikeash.com/pyblog/friday-qa-2015-02-06-locks-thread-safety-and-swift.html 什么是Swift相当于Objective-C的“@synchronized”?

做一个稍微不同的方式(这是从Xcode 9游乐场)是使用一个并发队列,而不是一个串行队列。

 final class MySingleton { static let shared = MySingleton() private let nameQueue = DispatchQueue(label: "name.accessor", qos: .default, attributes: .concurrent) private var _name = "Initial name" private init() {} var name: String { get { var name = "" nameQueue.sync { name = _name } return name } set { nameQueue.async(flags: .barrier) { self._name = newValue } } } } 
  • 使用并发队列意味着来自多个线程的多次读取不会彼此阻塞。 由于没有突变获得,价值可以同时阅读,因为…
  • 我们使用.barrierasynchronous调度来设置新的值。 该块可以asynchronous执行,因为调用者不需要等待值的设置。 该块将不会运行,直到其前面的并发队列中的所有其他块完成。 所以,当这个setter正在等待运行时,现有的挂起的读取不会受到影响。 障碍意味着当它开始运行时,不会有其他的程序块运行。 有效地,在setter的持续时间内将队列变成一个串行队列。 在这个模块完成之前不能进一步读取。 当这个块已经完成了新值的设置后,在这个设置者之后添加的任何获得者现在可以同时运行。