我相信XCode在我的SynchronizedDictionary中错误地报告了Swift Access Race-是吗?

我的SynchronizedDictionary看起来像这样:

public struct SynchronizedDictionary<K: Hashable, V> {
    private var dictionary = [K: V]()
    private let queue = DispatchQueue(
        label: "SynchronizedDictionary",
        qos: DispatchQoS.userInitiated,
        attributes: [DispatchQueue.Attributes.concurrent]
    )

    public subscript(key: K) -> V? {
        get {
            return queue.sync {
                return self.dictionary[key]
            }
        }
        mutating set {
            queue.sync(flags: .barrier) {
                self.dictionary[key] = newValue
            }
        }
    }
}


以下测试代码将触发“快速访问竞赛”问题(为该方案打开线程清理器时):

var syncDict = SynchronizedDictionary<String, String>()

let setExpectation = XCTestExpectation(description: "set_expectation")
let getExpectation = XCTestExpectation(description: "get_expectation")

let queue = DispatchQueue(label: "SyncDictTest", qos: .background, attributes: [.concurrent])

queue.async {
    for i in 0...100 {
        syncDict["\(i)"] = "\(i)"
    }
    setExpectation.fulfill()
}

queue.async {
    for i in 0...100 {
        _ = syncDict["\(i)"]
    }
    getExpectation.fulfill()
}

self.wait(for: [setExpectation, getExpectation], timeout: 30)


Swift Race Access看起来像这样:

swift - Xcode错误地报告Swift Access竞争情况-LMLPHP
我真的没想到这里会有访问竞争的情况,因为SynchronizedDictionary应该处理并发。

我可以通过在测试中将获取和设置包装在与SynchronizedDictionary的实际实现类似的DispatchQueue中来解决此问题:

let accessQueue = DispatchQueue(
    label: "AccessQueue",
    qos: DispatchQoS.userInitiated,
    attributes: [DispatchQueue.Attributes.concurrent]
)

var syncDict = SynchronizedDictionary<String, String>()

let setExpectation = XCTestExpectation(description: "set_expectation")
let getExpectation = XCTestExpectation(description: "get_expectation")

let queue = DispatchQueue(label: "SyncDictTest", qos: .background, attributes: [.concurrent])

queue.async {
    for i in 0...100 {
        accessQueue.sync(flags: .barrier) {
            syncDict["\(i)"] = "\(i)"
        }
    }
    setExpectation.fulfill()
}

queue.async {
    for i in 0...100 {
        accessQueue.sync {
            _ = syncDict["\(i)"]
        }
    }
    getExpectation.fulfill()
}

self.wait(for: [setExpectation, getExpectation], timeout: 30)


...但是这已经在SynchronizedDictionary内部发生了-那么Xcode为什么报告访问争用情况? -Xcode出错了,还是我错过了什么?

最佳答案

线程清理程序将Swift access race报告给

var syncDict = SynchronizedDictionary<String, String>()


结构,因为(通过下标设置器)在

syncDict["\(i)"] = "\(i)"


从一个线程访问,并在以下位置对同一结构(通过下标getter)进行只读访问

_ = syncDict["\(i)"]


从另一个线程,没有同步。

这与对private var dictionary属性的访问冲突或与下标方法内部发生的操作完全无关。如果将结构简化为以下内容,则将获得相同的“快速访问竞赛”

public struct SynchronizedDictionary<K: Hashable, V> {
    private let dummy = 1

    public subscript(key: String) -> String {
        get {
            return key
        }
        set {
        }
    }
}


因此,这是来自线程清理程序的正确报告,而不是错误。

一个可能的解决方案是定义一个类:

public class SynchronizedDictionary<K: Hashable, V> { ... }


这是一种引用类型,下标设置程序不再更改syncDict变量(现在是实际对象存储中的“指针”)。有了这一更改,您的代码便可以正常运行。

关于swift - Xcode错误地报告Swift Access竞争情况,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/58025624/

10-14 20:08
查看更多