#swift #multithreading #dictionary
#swift #многопоточность #словарь
Вопрос:
У меня возникла проблема с асинхронным объединением двух словарей. Идея состоит в том, чтобы выполнить функцию вычисления, которая возвращает словарь несколько раз асинхронно, а затем объединить результаты в один словарь. Я попытался выполнить следующее:
var subResult: [String: Result] = [:]
let stride = (max_val - min_val) / 2 1
DispatchQueue.concurrentPerform(iterations: stride) { index in
let c_size = max_val 2*index
var s_size = min_provided
var localResult: [String: Result] = [:]
repeat {
let res = SubFlow().process(data: data, cSize: c_size, sSize: s_size)
localResult.merge( res ) { (current, _) in current }
s_size = 2
} while (s_size <= c_size)
subResult.merge( localResult ) { (current, _) in current }
}
Это решение работает, но я не считаю его надежным, поскольку мы изменяем словарь асинхронно. Я новичок в Swift и не уверен, как я могу реализовать «безопасное» и эффективное слияние в этом случае?
Ответ №1:
Поскольку словари в Swift не являются потокобезопасными, вам необходимо убедиться, что все записи в словарь происходят в одной очереди.
Вы можете достичь этого либо путем создания последовательной очереди, либо путем создания параллельной очереди, убедившись при этом, что операции записи выполняются с барьерами. Последний подход позволит выполнять одновременное чтение из объекта, пока он не записывается в:
var subResult: [String: Int] = [:]
let resultUpdateQueue = DispatchQueue(label: "com.example.myapp.resultUpdateQueue", attributes: .concurrent)
DispatchQueue.concurrentPerform(iterations: 10) { index in
let localResult = ["sample(index)":index]
resultUpdateQueue.sync(flags: .barrier) {
subResult.merge( localResult ) { (current, _) in current }
}
}
print(subResult)
Убедитесь, что не выполняете .concurrentPerform()
в основной очереди, потому что она будет ждать, пока не завершатся все итерации.
Ответ №2:
Нет, это небезопасно. Это неопределенное поведение, и я несколько удивлен, что оно работает.
Вместо этого вы должны генерировать промежуточные результаты параллельно, а затем объединять их последовательно. Что-то вроде:
DispatchQueue.concurrentPerform(iterations: stride) { index in
// ... call process and generated localResults ...
serialQueue.dispatchAsync { subResult.merge ... }
}