Reputation: 23
Apple said "If you use Value type, you can safely pass copies of values across threads without synchronization.". But I saw concurrency crash at variance with the apple guide recently.
I saw https://developer.apple.com/swift/blog/?id=10 and Apple's guide that said "value type is safe in multithreads" so I thought "Value type is atomic!" but recently I saw concurrency crash in code below.
class ClassB: NSObject {
func readSomeValue() {
print(classA.someValue)
}
let classA = ClassA()
}
class ClassA: NSObject {
private(set) var someValue: StructA? {
didSet{
if oldValue != self.someValue { self.someFunction(self.someValue) }
}
}
private func modifySomeValue(_ newValue: StructA) {
self.someValue = newValue
}
}
struct StructA {
var a: Double
var b: String?
}
concurrency crash is occured when execute readSomeValue() in thread1 and execute modifySomeValue() in thread2. Why does concurrency crash happen? Is Value type not safe in multithreads?
Upvotes: 2
Views: 118
Reputation: 437792
A couple of observations:
That blog entry says:
Importantly, you can safely pass copies of values across threads without synchronization.
The operative word here is “copies”.
But in your example, you’re not passing copies of a value-type object to the different threads. You’re sharing single instance of reference-type object, a class
, between the threads. Sure, your reference-type has a value-type property, but that doesn’t alter the fact that you’re sharing a single instance of your reference-type object instance across threads. You will have to manually synchronize your interaction with that object and its properties in order to enjoy thread-safety.
There’s an argument to be made that many discussions mislead readers into thinking that Swift value-types always enjoy copy (or copy-on-write) semantics, and therefore always enjoy this thread safety feature. But you have to be careful, because there are several examples where you don’t get copy semantics. Your example of having a value-type property within a reference-type object is one example.
Another example is when you fail to use closure “capture lists”. For example, the following is not thread-safe as it is using the same value-type instance across multiple threads:
var object = StructA(a: 42, b: "foo")
DispatchQueue.global().async {
print(object)
}
object.b = "bar"
But by adding the capture list, the global queue will have its own copy of the object, restoring this thread-safety interaction across threads because each thread has its own copy of the object in question:
var object = StructA(a: 42, b: "foo")
DispatchQueue.global().async { [object] in
print(object)
}
object.b = "bar"
Yes, you can write thread-safe code if you (a) use value types; and (b) pass copies of these value types around. But this has nothing to do with atomicity. Bottom line, Swift variables are not atomic.
Upvotes: 2