Xcode 错误地报告 Swift 访问竞争条件 [英] Xcode Incorrectly Reporting Swift Access Race Condition

查看:49
本文介绍了Xcode 错误地报告 Swift 访问竞争条件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我认为 XCode 在我的 SynchronizedDictionary 中错误地报告了 Swift Access Race - 还是这样?

I believe XCode is incorrectly reporting Swift Access Race in my SynchronizedDictionary - or is it?

我的 SynchronizedDictionary 看起来像这样:

My SynchronizedDictionary looks like this:

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
            }
        }
    }
}

以下测试代码将触发Swift Access Race"问题(当为方案打开线程清理器时):

The following test code will trigger a "Swift Access Race" issue (when the Thread Sanitizer is turned on for the scheme):

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 如下所示:

The Swift Race Access look like this:

我真的没想到这里会出现访问竞争条件,因为 SynchronizedDictionary 应该处理并发.

I really did not expect there to be an access race condition here, because the SynchronizedDictionary should handle the concurrency.

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

I can fix the issue by, in the test, wrapping the getting and setting in a DispatchQueue similar to the actual implementation of the SynchronizedDictionary:

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 有问题,还是我遗漏了什么?

...but that already happens inside the SynchronizedDictionary - so why is Xcode reporting an Access Race Condition? - is Xcode at fault, or am I missing something?

推荐答案

The thread sanitizer 报告了一个 快速访问竞赛

The thread sanitizer reports a Swift access race to the

var syncDict = SynchronizedDictionary<String, String>()

结构,因为在

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

来自一个线程,对同一结构的只读访问(通过下标getter)在

from one thread, and a read-only access to the same structure (via the subscript getter) at

_ = syncDict["\(i)"]

来自不同的线程,没有同步.

from a different thread, without synchronization.

这与对 private var dictionary 属性的访问冲突无关,也与下标方法内部发生的事情完全无关.如果您将结构简化为

This has nothing to do with conflicting access to the private var dictionary property, or with what happens inside the subscript methods at all. You'll get the same "Swift access race" if you simplify the structure to

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

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

所以这是来自线程消毒程序的正确报告,而不是错误.

So this is a correct report from the thread sanitizer, not a bug.

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

A possible solution would be to define a class instead:

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

这是一个引用类型,下标设置器不再改变 syncDict 变量(它现在是一个指向实际对象存储的指针").有了这一更改,您的代码就可以正常运行.

That is a reference type and the subscript setter no longer mutates the syncDict variable (which is now a "pointer" into the actual object storage). With that change, your code runs without errors.

这篇关于Xcode 错误地报告 Swift 访问竞争条件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆