我有一个被多个线程共享的内存。我希望防止这些线程同时访问这段内存(就像生产者-消费者问题一样)。
问题:
一个线程向队列中添加元素,另一个线程读取这些元素并删除它们。它们不应该同时访问队列。
解决此问题的一种方法是使用互斥锁。
据我所知,Swift 中没有互斥锁。在 Swift 中是否有任何替代方案?
我有一个被多个线程共享的内存。我希望防止这些线程同时访问这段内存(就像生产者-消费者问题一样)。
问题:
一个线程向队列中添加元素,另一个线程读取这些元素并删除它们。它们不应该同时访问队列。
解决此问题的一种方法是使用互斥锁。
据我所知,Swift 中没有互斥锁。在 Swift 中是否有任何替代方案?
有很多解决方案,但我在这种情况下使用串行队列:
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()
感谢beshio的评论,你可以像这样使用信号量:
let semaphore = DispatchSemaphore(value: 1)
在使用资源之前,请使用wait:
semaphore.wait()
// use the resource
并且在使用 release 之后:
semaphore.signal()
在每个线程中都执行此操作。
正如其他人评论的那样(包括我),有几种方法可以实现这种类型的锁。但是我认为,与其他方法相比,调度信号量更好,因为它似乎具有最小的开销。在Apple的文档中找到(Replacing Semaphore Code),除非信号量已经被锁定(=零),否则它不会进入内核空间,只有在这种情况下代码才会进入内核以切换线程。我认为大多数情况下信号量不为零(当然这取决于应用程序特定的事情)。因此,我们可以避免很多开销。
对于调度信号量的另一个评论,这是与上述情况相反的场景。如果您的线程具有不同的执行优先级,并且较高优先级的线程必须长时间锁定信号量,则调度信号量可能不是解决方案。这是因为等待线程之间没有“队列”。在这种情况下发生的情况是,较高优先级的线程大部分时间获取并锁定信号量,而较低优先级的线程只有偶尔可以锁定信号量,因此大部分时间都在等待。如果这种行为对您的应用程序不好,您必须考虑使用调度队列。
您可以使用NSLock或NSRecursiveLock。如果需要从另一个锁定函数调用一个锁定函数,请使用递归版本。
class X {
let lock = NSLock()
func doSome() {
lock.lock()
defer { lock.unlock() }
//do something here
}
}
os_unfair_lock
是一个高效的通用互斥锁,特别适用于临界区较短的情况。它比队列要轻量得多(体积小了30倍),并且可以跟踪优先级,避免了DispatchSemaphore
可能出现的倒置问题。OSAllocatedUnfairLock
。如果这些对你来说不是一个选择,或者你对直接使用锁不太熟悉,那么NSLock
会增加一些开销,但也是一个不错的替代方案。特别是与队列或信号量相比 :)