Xcode неправильно сообщает о состоянии гонки Swift Access - PullRequest
0 голосов
/ 05 марта 2019

Я считаю, что XCode неправильно сообщает о Swift Access Race в моем SynchronizedDictionary - или это?

Мой 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
            }
        }
    }
}

Следующий тестовый код будетвызвать проблему «Swift Access Race» (когда для схемы включено средство очистки потока):

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 Race Access Я действительно не ожидал, что здесь будет условие гонки доступа, потому что SynchronizedDictionary должен обрабатывать параллелизм.

Я могу решить эту проблему, в тесте, оборачивая получение и настройку вDispatchQueue похож на фактическую реализацию 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 виноват или я что-то упустил?

1 Ответ

0 голосов
/ 27 марта 2019

Средство очистки потока сообщает о стремительной скорости доступа в структуру

var syncDict = SynchronizedDictionary<String, String>()

, поскольку существует мутантный доступ (через установщик индекса) на

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

из одного потока и доступ только для чтения к той же структуре (через средство получения индекса) в

_ = syncDict["\(i)"]

из другого потока, без синхронизации.

Это не имеет никакого отношения кделать с конфликтующим доступом к свойству private var dictionary или с тем, что происходит внутри методов индекса.Вы получите ту же «гонку доступа Swift», если упростите структуру до

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

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

Так что это правильный отчет из средства очистки потока, а не ошибка.

Возможным решением было бы определить class вместо этого:

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

Это ссылочный тип, и установщик нижних индексов больше не мутирует переменную syncDict (которая теперь«указатель» на фактическое хранилище объектов).С этим изменением ваш код выполняется без ошибок.

...