9

我在 Swift 中创建了一个“锁”,并为我的 Swift 类创建了一个使用该锁的 Atomic 属性包装器,因为 Swift 缺少 ObjC 的atomicproperty 属性。

当我在启用线程清理器的情况下运行测试时,它总是在使用我的 Atomic 属性包装器的属性上捕获数据竞争。

唯一有效的是将属性包装器的声明更改为类而不是结构,这里的主要问题是:为什么它有效!

print在属性包装器中添加了 s 和 lockinit来跟踪创建的对象的数量,它与 struct/class 相同,尝试在另一个项目中重现问题,也没有用。但我会添加与问题相似的文件,并让我知道它为什么起作用的任何猜测。

public class SwiftLock {

    init() { }

   public func sync<R>(execute: () throws -> R) rethrows -> R {
    objc_sync_enter(self)
    defer { objc_sync_exit(self) }
    return try execute()
    }
}

原子属性包装器

@propertyWrapper struct Atomic<Value> {
    let lock: SwiftLock
    var value: Value

    init(wrappedValue: Value, lock: SwiftLock=SwiftLock()) {
        self.value = wrappedValue
        self.lock = lock
    }

    var wrappedValue: Value {
        get {
            lock.sync { value }
        }
        set {
            lock.sync { value = newValue }
        }
    }
}

模型(数据竞争应该发生在publicVariable2此处的属性上)

class Model {
    @Atomic var publicVariable: TimeInterval = 0
    @Atomic var publicVariable2: TimeInterval = 0
    var sessionDuration: TimeInterval {
        min(0, publicVariable - publicVariable2)
    }
}

更新 1: 完整的 Xcode 项目:https ://drive.google.com/file/d/1IfAsOdHKOqfuOp-pSlP75FLF32iVraru/view?usp=sharing

4

1 回答 1

2

这是这个 PR 中回答的问题:https ://github.com/apple/swift-evolution/pull/1387

我认为这是真正解释它的那些台词

在 Swift 的正式内存访问模型中,值类型上的方法被认为是访问整个值,因此调用 WrappedValue 的 getter 会正式读取整个存储的包装器,而调用 WrappedValue 的 setter 会正式地修改整个存储的包装器。

包装器的值将在调用之前加载并在调用 wrappedValue.getter之后写回 wrappedValue.setter。因此,包装器内的同步不能提供对其自身值的原子访问。

于 2021-06-13T08:46:20.457 回答