Stateful Goroutines in Swift
Our first example demonstrates stateful goroutines using Swift’s concurrency features. This approach aligns with Swift’s ideas of sharing memory by communicating and having each piece of data owned by exactly 1 actor.
import Foundation
// These structs encapsulate read and write requests and provide a way for the owning actor to respond.
struct ReadOp {
let key: Int
let respond: (Int) -> Void
}
struct WriteOp {
let key: Int
let val: Int
let respond: (Bool) -> Void
}
actor StateManager {
private var state = [Int: Int]()
func read(_ key: Int) -> Int {
return state[key, default: 0]
}
func write(key: Int, value: Int) {
state[key] = value
}
}
@main
struct StatefulConcurrencyExample {
static func main() async {
let stateManager = StateManager()
var readOps = 0
var writeOps = 0
// Start 100 tasks to perform reads
for _ in 0..<100 {
Task {
while true {
let key = Int.random(in: 0..<5)
let _ = await stateManager.read(key)
readOps += 1
try? await Task.sleep(nanoseconds: 1_000_000) // Sleep for 1ms
}
}
}
// Start 10 tasks to perform writes
for _ in 0..<10 {
Task {
while true {
let key = Int.random(in: 0..<5)
let value = Int.random(in: 0..<100)
await stateManager.write(key: key, value: value)
writeOps += 1
try? await Task.sleep(nanoseconds: 1_000_000) // Sleep for 1ms
}
}
}
// Let the tasks work for a second
try? await Task.sleep(nanoseconds: 1_000_000_000)
// Report the operation counts
print("readOps:", readOps)
print("writeOps:", writeOps)
}
}
Running our program shows that the actor-based state management example completes a certain number of total operations:
$ swift run
readOps: 71708
writeOps: 7177
For this particular case, the actor-based approach in Swift provides a safe and efficient way to manage shared state. It eliminates the need for explicit locking while still ensuring thread-safe access to the shared data. This approach aligns well with Swift’s concurrency model, making it easier to reason about the correctness of your program.
Actors in Swift serve a similar purpose to goroutines in Go for this example. They provide a way to encapsulate state and ensure that it’s accessed in a thread-safe manner. The @main
attribute and static func main()
are used as the entry point for the Swift program, similar to the main()
function in Go.
Remember to use whichever approach feels most natural and makes it easiest to understand and ensure the correctness of your program.