Это интересно, я узнаю об этом впервые.
Проблема в первом бите кода заключается в том, что:
object.value += 1
имеет ту же семантику, что и
object.value = object.value + 1
, которую мы можем расширить до:
let originalValue = queue.sync { object._value }
let newValue = origiinalValue + 1
queue.sync { self._value = newValue }
Расширяя ее, вы понимаете, что синхронизация геттера и сеттера работают нормально, но они не синхронизированы в целом. Переключение контекста в середине кода выше может привести к мутации _value
другим потоком, без изменения newValue
.
Использование блокировки будет иметь ту же проблему. Это расширилось бы до:
lock.lock()
let originalValue = object._value
lock.unlock()
let newValue = originalValue + 1
lock.lock()
object._value = newValue
lock.unlock()
Вы можете убедиться в этом сами, оснастив свой код некоторыми инструкциями регистрации, которые показывают, что мутация не полностью покрыта замком:
class C {
var lock = NSLock()
var _value: Int
var value: Int {
get {
print("value.get start")
print("lock.lock()")
lock.lock()
defer {
print("lock.unlock()")
lock.unlock()
print("value.get end")
}
print("getting self._value")
return self._value
}
set {
print("\n\n\nvalue.set start")
lock.lock()
print("lock.lock()")
defer {
print("lock.unlock()")
lock.unlock()
print("value.set end")
}
print("setting self._value")
self._value = newValue
}
}
init(_ value: Int) { self._value = value }
}
let object = C(0)
object.value += 1