swift 中的互斥替代方案
Mutex alternatives in swift
我有多个线程之间的共享内存。我想防止这些线程同时访问这块内存。 (比如生产者-消费者问题)
问题:
一个线程向队列添加元素,另一个线程读取这些元素并删除它们。他们不应同时访问队列。
这个问题的一个解决方案是使用 Mutex。
正如我所发现的,Swift 中没有 Mutex。 Swift有没有其他选择?
对此有很多解决方案,但我对这种操作使用串行队列:
let serialQueue = DispatchQueue(label: "queuename")
serialQueue.sync {
//call some code here, I pass here a closure from a method
}
Edit/Update:也适用于信号量:
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)
使用资源前等待:
semaphore.wait()
// use the resource
使用后释放它:
semaphore.signal()
在每个线程中执行此操作。
正如人们评论的那样(包括我),有几种方法可以实现这种锁定。但我认为 dispatch semaphore 比其他的要好,因为它似乎开销最少。正如在 Apples doc, "Replacing Semaphore Code" 中发现的那样,它不会进入内核 space 除非信号量已经被锁定(= 零),这是代码进入内核以切换信号量的唯一情况线。我认为大多数时候信号量不为零(不过这当然是特定于应用程序的问题)。因此,我们可以避免很多开销。
再评论一下dispatch semaphore,和上面相反的场景。如果您的线程具有不同的执行优先级,并且优先级较高的线程必须长时间锁定信号量,则分派信号量可能不是解决方案。这是因为等待线程中没有 "queue"。在这种情况下发生的是更高的优先级
线程大部分时间获取并锁定信号量,低优先级线程只能偶尔锁定信号量,因此,大部分时间只是等待。如果这种行为对您的应用程序不利,您必须考虑使用调度队列。
您可以使用 NSLock 或 NSRecursiveLock。如果您需要从另一个锁定函数调用一个锁定函数,请使用递归版本。
class X {
let lock = NSLock()
func doSome() {
lock.lock()
defer { lock.unlock() }
//do something here
}
}
我有多个线程之间的共享内存。我想防止这些线程同时访问这块内存。 (比如生产者-消费者问题)
问题:
一个线程向队列添加元素,另一个线程读取这些元素并删除它们。他们不应同时访问队列。
这个问题的一个解决方案是使用 Mutex。
正如我所发现的,Swift 中没有 Mutex。 Swift有没有其他选择?
对此有很多解决方案,但我对这种操作使用串行队列:
let serialQueue = DispatchQueue(label: "queuename")
serialQueue.sync {
//call some code here, I pass here a closure from a method
}
Edit/Update:也适用于信号量:
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)
使用资源前等待:
semaphore.wait()
// use the resource
使用后释放它:
semaphore.signal()
在每个线程中执行此操作。
正如人们评论的那样(包括我),有几种方法可以实现这种锁定。但我认为 dispatch semaphore 比其他的要好,因为它似乎开销最少。正如在 Apples doc, "Replacing Semaphore Code" 中发现的那样,它不会进入内核 space 除非信号量已经被锁定(= 零),这是代码进入内核以切换信号量的唯一情况线。我认为大多数时候信号量不为零(不过这当然是特定于应用程序的问题)。因此,我们可以避免很多开销。
再评论一下dispatch semaphore,和上面相反的场景。如果您的线程具有不同的执行优先级,并且优先级较高的线程必须长时间锁定信号量,则分派信号量可能不是解决方案。这是因为等待线程中没有 "queue"。在这种情况下发生的是更高的优先级 线程大部分时间获取并锁定信号量,低优先级线程只能偶尔锁定信号量,因此,大部分时间只是等待。如果这种行为对您的应用程序不利,您必须考虑使用调度队列。
您可以使用 NSLock 或 NSRecursiveLock。如果您需要从另一个锁定函数调用一个锁定函数,请使用递归版本。
class X {
let lock = NSLock()
func doSome() {
lock.lock()
defer { lock.unlock() }
//do something here
}
}