Section 8/161 menit
8. Shared Mutable State — Pola Aman
8. Shared Mutable State — Pola Aman
Pola 1: Actor untuk Shared State
swift
// Shared cache yang aman di-access dari banyak concurrent task
actor ImageCache {
private var storage: [URL: CachedImage] = [:]
private var ongoingRequests: [URL: Task<CachedImage, Error>] = [:]
func image(for url: URL) async throws -> CachedImage {
// Hit cache
if let cached = storage[url] {
return cached
}
// Deduplicate concurrent requests untuk URL yang sama
if let ongoingTask = ongoingRequests[url] {
return try await ongoingTask.value
}
// Mulai request baru
let task = Task<CachedImage, Error> {
let (data, _) = try await URLSession.shared.data(from: url)
return CachedImage(data: data, url: url)
}
ongoingRequests[url] = task
do {
let image = try await task.value
storage[url] = image
ongoingRequests.removeValue(forKey: url)
return image
} catch {
ongoingRequests.removeValue(forKey: url)
throw error
}
}
func invalidate(url: URL) {
storage.removeValue(forKey: url)
}
func invalidateAll() {
storage.removeAll()
}
}
Pola 2: Mutex untuk Low-Overhead Synchronization
swift
import Synchronization
// Mutex dari Swift 6 Synchronization framework
// Lebih ringan dari actor untuk operasi yang sangat singkat
final class ThreadSafeCounter: Sendable {
private let value = Mutex<Int>(0)
func increment() {
value.withLock { $0 += 1 }
}
func decrement() {
value.withLock { $0 -= 1 }
}
var current: Int {
value.withLock { $0 }
}
}
// Mutex untuk state yang lebih kompleks
final class RequestTracker: Sendable {
struct State {
var activeRequests: Int = 0
var totalCompleted: Int = 0
var errors: [Error] = []
}
private let state = Mutex<State>(State())
func requestStarted() {
state.withLock { $0.activeRequests += 1 }
}
func requestCompleted() {
state.withLock {
$0.activeRequests -= 1
$0.totalCompleted += 1
}
}
func requestFailed(with error: Error) {
state.withLock {
$0.activeRequests -= 1
$0.errors.append(error)
}
}
var snapshot: State {
state.withLock { $0 }
}
}
Pola 3: Copy-on-Write Value Types
swift
// Value types dengan COW semantics secara otomatis thread-safe untuk reads
// karena setiap thread mendapat copy-nya sendiri
struct DocumentState {
private var _content: String
private var _metadata: [String: String]
// Sendable karena semua stored properties Sendable
init(content: String = "", metadata: [String: String] = [:]) {
self._content = content
self._metadata = metadata
}
mutating func updateContent(_ newContent: String) {
_content = newContent
}
mutating func setMetadata(key: String, value: String) {
_metadata[key] = value
}
var content: String { _content }
var metadata: [String: String] { _metadata }
}
// Actor yang menyimpan value type — akses concurrent aman
actor DocumentStore {
private var documents: [DocumentID: DocumentState] = [:]
func update(_ id: DocumentID, with state: DocumentState) {
documents[id] = state
}
func document(for id: DocumentID) -> DocumentState? {
documents[id] // Return copy — caller mendapat snapshot immutable
}
}
Pola 4: Atomic untuk Simple Primitives
swift
import Synchronization
// Atomic untuk counter, flags, sequence numbers
final class SequenceGenerator: Sendable {
private let counter = Atomic<Int>(0)
func next() -> Int {
// Atomic fetch-and-add — tidak butuh lock
counter.fetchAdd(1, ordering: .relaxed)
}
}
// Atomic flag untuk cancellation
final class CancellationToken: Sendable {
private let cancelled = Atomic<Bool>(false)
func cancel() {
cancelled.store(true, ordering: .releasing)
}
var isCancelled: Bool {
cancelled.load(ordering: .acquiring)
}
}