5

The following piece of code supposedly sets up a thread-safe singleton:

class Singleton {
    static var shared = Singleton()
    private let internalQueue = DispatchQueue(label: "SingletionInternalQueue", qos: .default, attributes: .concurrent)

    private var _foo: String = "aaa"

    var foo: String {
        get {
            return internalQueue.sync { _foo }
        }
        set (newState) {
            internalQueue.async(flags: .barrier) { self._foo = newState }
        }
    }

    func setup(string: String) {
        foo = string
    }
}

Source: Thread safe singleton in swift

But I don't understand the purpose of this.

For example, if I wish to get the value of foo, shouldn't I be able to just read it? The operation should always be performed on the main thread, so what is the point of adding another thread?

Similarly with set: if I am worried about multiple sources setting the value at the same time, can't we simplify that code and eliminate the .barrier parameter?

internalQueue.async(flags: .barrier) { self._foo = newState}

If it is in a sync block, doesn't this force it onto the main thread? If so, then why wouldn't the code only need to be self._foo = newState?

Ahmad F
  • 30,560
  • 17
  • 97
  • 143
progammingBeignner
  • 936
  • 1
  • 8
  • 19
  • *"Since getting the foo value will always be performed in main queue"* - if you only use this singleton from the main queue then there is no need for any synchronizing. The whole point is to make it safe to use the singleton concurrently on several different threads. – rmaddy Nov 12 '18 at 06:21
  • I'm wondering if your question is being misread. When stating *"Since getting the foo value will always be performed in main queue"* are you stating (1) you will only be doing this or (2) that all such gets only occur on the main queue by language design? – CRD Nov 12 '18 at 08:17

1 Answers1

0

You are correct: As long as you only access the singleton from the main thread, there is no need for synchronization. In that case, I'd prefer to make that an explicit requirement by using code like this:

assert(Thread.isMainThread)

This will be optimized away in production builds, but will make sure you pick up inadvertent programming errors in debug mode (e.g. when you call your code from a URLSession completion handler).

herzi
  • 774
  • 6
  • 18