In the past, thread-safe code in Swift often relied on Grand Central Dispatch (GCD). Let's explore an example:
import Foundation
class ReadWriteLock {
private var concurrentQueue = DispatchQueue(label: "com.example.readWriteLock", attributes: .concurrent)
private var value: Int = 0
func read() -> Int {
var result: Int = 0
concurrentQueue.sync {
result = value
}
return result
}
func write(_ newValue: Int) {
concurrentQueue.async(flags: .barrier) {
self.value = newValue
}
}
}
In this example, the ReadWriteLock class offers two methods: read() and write(_:).
- The
read()method retrieves the current value of thevalueproperty in a thread-safe manner using synchronous dispatch. - The
write(_:)method updates thevalueproperty with a new value, also in a thread-safe manner, using asynchronous dispatch with the.barrierflag. This ensures exclusive access during write operations, preventing race conditions while allowing multiple readers simultaneous access.
With the introduction of Swift 5.5, concurrency brought forth a simpler approach to writing thread-safe code. Let's reimagine the previous example using concurrency:
import Foundation
actor ReadWriteActor {
private var value: Int = 0
func read() -> Int {
return value
}
func write(_ newValue: Int) {
value = newValue
}
}
In this version, ReadWriteActor is declared as an actor. Actors ensure state isolation, allowing only one task to access the actor's mutable state at a time. This eliminates the need for manual synchronization mechanisms like locks or GCD queues.
The read() method simply returns the current value of the value property, while the write(_:) method updates the value property with a new value. Access to the value property is automatically serialized by the actor, ensuring thread safety without the need for explicit locking or synchronization.
No comments:
Post a Comment