Swift中的Mutex替代方案

amrnrhlw  于 2023-11-16  发布在  Swift
关注(0)|答案(5)|浏览(126)

我在多个线程之间有一个共享内存。我想防止这些线程同时访问这片内存。(类似于 * 生产者-消费者 * 问题)

问题

一个线程将元素添加到队列中,另一个线程读取这些元素并删除它们。它们不应该同时访问队列。
解决这个问题的一个方法是使用Mutex。
正如我发现的,Swift中没有Mutex。Swift中有替代品吗?

gr8qqesn

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()

pvabu6sv

pvabu6sv2#

感谢beshio的评论,你可以像这样使用信号量:

let semaphore = DispatchSemaphore(value: 1)

字符串
在使用资源之前使用wait:

semaphore.wait()
// use the resource


使用后释放:

semaphore.signal()


在每个线程中执行此操作。

xuo3flqw

xuo3flqw3#

正如人们评论的那样,(包括我),有几种方法可以实现这种锁定。但我认为分派信号量比其他方法更好,因为它似乎具有最小的开销。正如在Apple文档中找到的,“替换信号量代码”,它不会进入内核空间,除非信号量已经锁定(=零),这是代码进入内核切换线程的唯一情况。我认为信号量在大多数情况下都不是零(当然这是应用程序特有的问题)。因此,我们可以避免大量的开销。
关于分派信号量还有一个评论,这是与上面相反的情况。如果你的线程有不同的执行优先级,并且高优先级的线程必须长时间锁定信号量,分派信号量可能不是解决方案。这是因为在等待的线程之间没有“队列”。在这种情况下发生的是,高优先级的线程在大部分时间获得并锁定信号量,而低优先级的线程只能偶尔锁定信号量,因此,大多数情况下只是等待。2如果这种行为对你的应用程序不好,你必须考虑调度队列。

rdrgkggo

rdrgkggo4#

您可以使用NSLock或NSRecursiveLock。如果您需要从一个锁定函数调用另一个锁定函数,请使用递归版本。

class X {
  let lock = NSLock()

  func doSome() {
    lock.lock()
    defer { lock.unlock() }
    //do something here
  }

}

字符串

jobtbby3

jobtbby35#

在现代平台(macOS 10.12+,iOS 10+)上,os_unfair_lock是一个高性能,高效的通用互斥体,特别是当你的临界区很短时。它比队列更轻量级(小30倍),并跟踪优先级,防止DispatchSemaphore可能发生的反转。
与大多数低级同步原语一样,它需要有一个稳定的地址,因此您应该自己分配它,或者可以使用OSAllocatedUnfairLock(如果较新的(macOS 13+,iOS 16+)系统。如果这些不适合你,或者你不太习惯直接使用锁,NSLock只增加了少量的开销,但并不是一个坏的选择。特别是与队列或信号量相比:)

相关问题