I know, global variables are "not sexy", but I have few in my current project. I played around with Xcode's Thread Sanitizer and found a data race on them. So I tried to make them thread safe.
As I also want a single point of management for this variables. I tried to do the GCD stuff in getter and setter of the variables.
Finally I found a solution that worked, was accepted by the compiler and the Thread Sanitizer was happy .... BUT... this solution looks quite ugly (see below) and is very slow (did a performance test and it was VERY slow).
Yes, I know, if I use classes for this it might be more "swifty", but there must be an easy solution for a thread safe global variable.
So would you be so kind and give hints and suggestions to optimize this attempt? Anyt hint / idea/ suggestion / comment is welcomed!
// I used a "computed variable", to overcome the compiler errors,
// we need a helper variable to store the actual value.
var globalVariable_Value : Int = 0
// this is the global "variable" we worked with
var globalVariable : Int {
// the setter
set (newValue) {
globalDataQueue.async(flags: .barrier) {
globalVariable_Value = newValue
}
}
// the getter
get {
// we need a helper variable to store the result.
// inside a void closure you are not allow to "return"
var result : Int = 0
globalDataQueue.sync{
result = globalVariable_Value
}
return result
}
}
// usage
globalVariable = 1
print ("globalVariable == \(globalVariable)")
globalVariable += 1
print ("globalVariable == \(globalVariable)")
// output
// globalVariable == 1
// globalVariable == 2
OOPer asked me to redo the performance tests as found the result strange.
Well, he was right. I did write a simple app (Performance Test App on GitHub) and attached some screenshots.
I run that tests on an iPhone SE with latest IOS. The app was started on the device, not in Xcode. compiler settings were "debug" for all shown test results. I did also test with "full optimizations" (smallest fastest [-Os]), but the results were very similar. I think in that simple tests is not much to optimize.
The test app simply runs the tests described in the answer above. To make it a little bit more realistic, it is doing each test on three parallel async dispatched queues with the qos classes .userInteractive, .default and .background.
There might be better ways to test such things. But for the purpose of this question, I think it's good enough.
I'm happy if anybody would reassess the code and maybe find better test algorithms ... we all would learn from it. I stop my work on this now.
The results are quite strange in my eyes. All three different approaches gives roughly the same performance. On each run there was another "hero", so I assume it is just influenced by other background tasks etc. So even Itai Ferber "nice" solution has in practice no benefit. It's "just" a more elegant code.
And yes, the thread save solution is WAY slower than the not queued solution.
This is the main learning: Yes, it's possible to make global variables thread safe, but there is a significant performance issue with it.




If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With