我在多个线程之间有一个共享内存。我想防止这些线程同时访问这片内存。(类似于 * 生产者-消费者 * 问题)
问题:
一个线程将元素添加到队列中,另一个线程读取这些元素并删除它们。它们不应该同时访问队列。解决这个问题的一个方法是使用Mutex。正如我发现的,Swift中没有Mutex。Swift中有替代品吗?
gr8qqesn1#
有很多解决方案,但我使用串行队列来执行这种操作:
let serialQueue = DispatchQueue(label: "queuename") serialQueue.sync { //call some code here, I pass here a closure from a method }
字符串编辑/更新:也适用于信号量:
let higherPriority = DispatchQueue.global(qos: .userInitiated) let lowerPriority = DispatchQueue.global(qos: .utility) let semaphore = DispatchSemaphore(value: 1) func letUsPrint(queue: DispatchQueue, symbol: String) { queue.async { debugPrint("\(symbol) -- waiting") semaphore.wait() // requesting the resource for i in 0...10 { print(symbol, i) } debugPrint("\(symbol) -- signal") semaphore.signal() // releasing the resource } } letUsPrint(queue: lowerPriority, symbol: "Low Priority Queue Work") letUsPrint(queue: higherPriority, symbol: "High Priority Queue Work") RunLoop.main.run()
型
pvabu6sv2#
感谢beshio的评论,你可以像这样使用信号量:
let semaphore = DispatchSemaphore(value: 1)
字符串在使用资源之前使用wait:
semaphore.wait() // use the resource
型使用后释放:
semaphore.signal()
型在每个线程中执行此操作。
xuo3flqw3#
正如人们评论的那样,(包括我),有几种方法可以实现这种锁定。但我认为分派信号量比其他方法更好,因为它似乎具有最小的开销。正如在Apple文档中找到的,“替换信号量代码”,它不会进入内核空间,除非信号量已经锁定(=零),这是代码进入内核切换线程的唯一情况。我认为信号量在大多数情况下都不是零(当然这是应用程序特有的问题)。因此,我们可以避免大量的开销。关于分派信号量还有一个评论,这是与上面相反的情况。如果你的线程有不同的执行优先级,并且高优先级的线程必须长时间锁定信号量,分派信号量可能不是解决方案。这是因为在等待的线程之间没有“队列”。在这种情况下发生的是,高优先级的线程在大部分时间获得并锁定信号量,而低优先级的线程只能偶尔锁定信号量,因此,大多数情况下只是等待。2如果这种行为对你的应用程序不好,你必须考虑调度队列。
rdrgkggo4#
您可以使用NSLock或NSRecursiveLock。如果您需要从一个锁定函数调用另一个锁定函数,请使用递归版本。
class X { let lock = NSLock() func doSome() { lock.lock() defer { lock.unlock() } //do something here } }
字符串
jobtbby35#
在现代平台(macOS 10.12+,iOS 10+)上,os_unfair_lock是一个高性能,高效的通用互斥体,特别是当你的临界区很短时。它比队列更轻量级(小30倍),并跟踪优先级,防止DispatchSemaphore可能发生的反转。与大多数低级同步原语一样,它需要有一个稳定的地址,因此您应该自己分配它,或者可以使用OSAllocatedUnfairLock(如果较新的(macOS 13+,iOS 16+)系统。如果这些不适合你,或者你不太习惯直接使用锁,NSLock只增加了少量的开销,但并不是一个坏的选择。特别是与队列或信号量相比:)
os_unfair_lock
DispatchSemaphore
OSAllocatedUnfairLock
NSLock
5条答案
按热度按时间gr8qqesn1#
有很多解决方案,但我使用串行队列来执行这种操作:
字符串
编辑/更新:也适用于信号量:
型
pvabu6sv2#
感谢beshio的评论,你可以像这样使用信号量:
字符串
在使用资源之前使用wait:
型
使用后释放:
型
在每个线程中执行此操作。
xuo3flqw3#
正如人们评论的那样,(包括我),有几种方法可以实现这种锁定。但我认为分派信号量比其他方法更好,因为它似乎具有最小的开销。正如在Apple文档中找到的,“替换信号量代码”,它不会进入内核空间,除非信号量已经锁定(=零),这是代码进入内核切换线程的唯一情况。我认为信号量在大多数情况下都不是零(当然这是应用程序特有的问题)。因此,我们可以避免大量的开销。
关于分派信号量还有一个评论,这是与上面相反的情况。如果你的线程有不同的执行优先级,并且高优先级的线程必须长时间锁定信号量,分派信号量可能不是解决方案。这是因为在等待的线程之间没有“队列”。在这种情况下发生的是,高优先级的线程在大部分时间获得并锁定信号量,而低优先级的线程只能偶尔锁定信号量,因此,大多数情况下只是等待。2如果这种行为对你的应用程序不好,你必须考虑调度队列。
rdrgkggo4#
您可以使用NSLock或NSRecursiveLock。如果您需要从一个锁定函数调用另一个锁定函数,请使用递归版本。
字符串
jobtbby35#
在现代平台(macOS 10.12+,iOS 10+)上,
os_unfair_lock
是一个高性能,高效的通用互斥体,特别是当你的临界区很短时。它比队列更轻量级(小30倍),并跟踪优先级,防止DispatchSemaphore
可能发生的反转。与大多数低级同步原语一样,它需要有一个稳定的地址,因此您应该自己分配它,或者可以使用
OSAllocatedUnfairLock
(如果较新的(macOS 13+,iOS 16+)系统。如果这些不适合你,或者你不太习惯直接使用锁,NSLock
只增加了少量的开销,但并不是一个坏的选择。特别是与队列或信号量相比:)