gpt4 book ai didi

swift - 当数组在Swift中并发读取时,如何实现removeAtIndex的线程安全性?

转载 作者:行者123 更新时间:2023-12-03 13:15:51 26 4
gpt4 key购买 nike

我在看this answer,它为具有并发读取的线程安全数组提供代码。正如@tombardey在评论中指出的那样,代码(下面的相关代码段)并不完全安全:

public func removeAtIndex(index: Int) {

self.accessQueue.async(flags:.barrier) {
self.array.remove(at: index)
}
}

public var count: Int {
var count = 0

self.accessQueue.sync {
count = self.array.count
}

return count
}

...Say the sychronized array hasone element, wouldn't this fail? if synchronizedArray.count == 1 {synchronizedArray.remove(at: 0) } It's a race condition, say twothreads execute the statement. Both read a count of 1 concurrently,both enqueue a write block concurrently. The write blocks executesequentially, the second one will fail... (cont.)


@Rob回复:

@tombardey - You are absolutely right that this level ofsynchronization (at the property/method level) is frequentlyinsufficient to achieve true thread-safety in broader applications.Your example is easily solved (by adding an method that dispatchesblock to the queue), but there are others that aren't (e.g."synchronized" array simultaneously used by a UITableViewDataSourceand mutated by some background operation). In those cases, you have toimplement your own higher-level synchronization. But the abovetechnique is nonetheless very useful in certain, highly constrainedsituations.


我正在努力弄清楚@Rob的含义是“您的示例很容易解决(通过添加一种将块分配到队列中的方法)”。我将很高兴看到此方法(或任何其他)技术的示例实现,以解决该问题。

最佳答案

你说:

I am struggling to work out what @Rob means by “Your example is easily solved (by adding [a] method that dispatches block to the queue)”. I would be interested to see an example implementation of this method (or any other) technique to solve the problem.


让我们扩展我为回答您的其他问题而发布的示例(请参阅 this answer中的第3点),添加更多的 Array方法:
class SynchronizedArray<T> {
private var array: [T]
private let accessQueue = DispatchQueue(label: "com.domain.app.reader-writer", attributes: .concurrent)

init(_ array: [T] = []) {
self.array = array
}

subscript(index: Int) -> T {
get { reader { $0[index] } }
set { writer { $0[index] = newValue } }
}

var count: Int {
reader { $0.count }
}

func append(newElement: T) {
writer { $0.append(newElement) }
}

func remove(at index: Int) {
writer { $0.remove(at: index) }
}

func reader<U>(_ block: ([T]) throws -> U) rethrows -> U {
try accessQueue.sync { try block(array) }
}

func writer(_ block: @escaping (inout [T]) -> Void) {
accessQueue.async(flags: .barrier) { block(&self.array) }
}
}
因此,假设您想删除一个项目(如果数组中只有一个项目)。考虑:
let numbers = SynchronizedArray([42])

...

if numbers.count == 1 {
numbers.remove(at: 0)
}
看起来足够纯真的,但是它不是线程安全的。如果其他线程正在插入或删除值,则可能会出现竞争状况。例如,如果其他一些线程在您测试 count的时间与您去掉该值的时间之间附加了一个值。
您可以通过将整个操作( if测试和后续删除)包装在一个同步的块中来解决此问题。因此,您可以:
numbers.writer { array in
if array.count == 1 {
array.remove(at: 0)
}
}
这个 writer方法(在这种基于读取器-写入器的同步中)是我所说的“将块分配到队列的方法”的示例。

现在,很明显,您还可以给自己的 SynchronizedArray提供自己的方法,例如:
func safelyRemove(at index: Int) {
writer { array in
if index < array.count {
array.remove(at: index)
}
}
}
然后,您可以执行以下操作:
numbers.safelyRemove(at: index)
...并且这是线程安全的,但仍然享有读写器同步的性能优势。
但是通常的想法是,在处理线程安全的集合时,您总是希望在更高的抽象级别上执行一系列要同步在一起的任务。通过公开 readerwriter的同步方法,您可以执行一个简单的通用机制。

正如其他人所说的那样,编写线程安全代码的最佳方法是完全避免并发访问。但是,如果必须使可变对象成为线程安全的,则调用者有责任确定必须作为单个同步操作执行的一系列任务。

关于swift - 当数组在Swift中并发读取时,如何实现removeAtIndex的线程安全性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65268602/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com