我已经搜索了Swift的书籍,但找不到@synchronized的Swift版本。我如何做互斥在Swift?


当前回答

随着Swift并发的出现,我们将使用actor。

You can use tasks to break up your program into isolated, concurrent pieces. Tasks are isolated from each other, which is what makes it safe for them to run at the same time, but sometimes you need to share some information between tasks. Actors let you safely share information between concurrent code. Like classes, actors are reference types, so the comparison of value types and reference types in Classes Are Reference Types applies to actors as well as classes. Unlike classes, actors allow only one task to access their mutable state at a time, which makes it safe for code in multiple tasks to interact with the same instance of an actor. For example, here’s an actor that records temperatures: actor TemperatureLogger { let label: String var measurements: [Int] private(set) var max: Int init(label: String, measurement: Int) { self.label = label self.measurements = [measurement] self.max = measurement } } You introduce an actor with the actor keyword, followed by its definition in a pair of braces. The TemperatureLogger actor has properties that other code outside the actor can access, and restricts the max property so only code inside the actor can update the maximum value.

要了解更多信息,请参见WWDC视频《使用Swift actor保护可变状态》。


为了完整起见,历史上的替代方案包括:

GCD serial queue: This is a simple pre-concurrency approach to ensure that one one thread at a time will interact with the shared resource. Reader-writer pattern with concurrent GCD queue: In reader-writer patterns, one uses a concurrent dispatch queue to perform synchronous, but concurrent, reads (but concurrent with other reads only, not writes) but perform writes asynchronously with a barrier (forcing writes to not be performed concurrently with anything else on that queue). This can offer a performance improvement over a simple GCD serial solution, but in practice, the advantage is modest and comes at the cost of additional complexity (e.g., you have to be careful about thread-explosion scenarios). IMHO, I tend to avoid this pattern, either sticking with the simplicity of the serial queue pattern, or, when the performance difference is critical, using a completely different pattern. Locks: In my Swift tests, lock-based synchronization tends to be substantially faster than either of the GCD approaches. Locks come in a few flavors: NSLock is a nice, relatively efficient lock mechanism. In those cases where performance is of paramount concern, I use “unfair locks”, but you must be careful when using them from Swift (see https://stackoverflow.com/a/66525671/1271826). For the sake of completeness, there is also the recursive lock. IMHO, I would favor simple NSLock over NSRecursiveLock. Recursive locks are subject to abuse and often indicate code smell. You might see references to “spin locks”. Many years ago, they used to be employed where performance was of paramount concern, but they are now deprecated in favor of unfair locks. Technically, one can use semaphores for synchronization, but it tends to be the slowest of all the alternatives.

我在这里概述了我的一些基准测试结果。

简而言之,现在我在当代代码库中使用actor,在简单的非异步等待代码中使用GCD串行队列,在性能至关重要的极少数情况下使用锁。

不用说,我们经常尝试减少同步的数量。如果可以,我们通常使用值类型,其中每个线程获得自己的副本。在无法避免同步的情况下,我们会尽量减少同步的数量。

其他回答

你可以把语句夹在objc_sync_enter(obj: AnyObject?)和objc_sync_exit(obj: AnyObject?)之间。@synchronized关键字在幕后使用这些方法。即。

objc_sync_enter(self)
... synchronized code ...
objc_sync_exit(self)

使用Bryan McLemore的答案,我扩展了它,以支持使用Swift 2.0延迟能力扔进安全庄园的对象。

func synchronized( lock:AnyObject, block:() throws -> Void ) rethrows
{
    objc_sync_enter(lock)
    defer {
        objc_sync_exit(lock)
    }

    try block()
}

您可以创建propertyWrapper synchronized

这里是NSLock下罩的例子。你可以使用任何你想要的同步GCD, posix_locks等

@propertyWrapper public struct Synchronised<T> {
    private let lock = NSLock()

    private var _wrappedValue: T
    public var wrappedValue: T {
        get {
            lock.lock()
            defer {
                lock.unlock()
            }
            return _wrappedValue
        }
        set {
            lock.lock()
            defer {
                lock.unlock()
            }
            _wrappedValue = newValue
        }
    }

    public init(wrappedValue: T) {
        self._wrappedValue = wrappedValue
    }
}

@Synchronised var example: String = "testing"

基于@drewster的答案

斯威夫特4

在Swift 4中,你可以使用gcd调度队列来锁定资源。

class MyObject {
    private var internalState: Int = 0
    private let internalQueue: DispatchQueue = DispatchQueue(label:"LockingQueue") // Serial by default

    var state: Int {
        get {
            return internalQueue.sync { internalState }
        }

        set (newState) {
            internalQueue.sync { internalState = newState }
        }
    }
} 

我喜欢并使用了这里的许多答案,所以我会选择最适合你的。也就是说,当我需要objective-c的@synchronized时,我更喜欢使用swift 2中引入的defer语句。

{ 
    objc_sync_enter(lock)
    defer { objc_sync_exit(lock) }

    //
    // code of critical section goes here
    //

} // <-- lock released when this block is exited

这个方法的好处是,你的临界区可以以任何想要的方式退出包含块(例如,return, break, continue, throw),并且“不管程序控制如何转移,defer语句中的语句都将被执行”。1