Section 7/141 menit
7. Concurrency dan Session Thread Safety
7. Concurrency dan Session Thread Safety
Session dan Actor Isolation
LanguageModelSession adalah class — bukan Sendable secara default. Gunakan @MainActor atau custom actor untuk akses yang aman:
swift
// ❌ Tidak aman: session diakses dari berbagai thread tanpa isolation
class UnsafeViewModel {
let session = LanguageModelSession()
func processInput(_ text: String) {
Task {
let result = try await session.respond(to: text) // Race condition!
await MainActor.run { updateUI(result) }
}
Task {
let result = try await session.respond(to: "lainnya") // Concurrent access!
}
}
}
swift
// ✓ Aman: session di-isolate ke @MainActor
@MainActor
@Observable
class SafeViewModel {
private let session = LanguageModelSession()
var isProcessing = false
var lastResponse = ""
func processInput(_ text: String) async throws {
guard !isProcessing else { return }
isProcessing = true
defer { isProcessing = false }
// Session hanya diakses dari MainActor — tidak ada race condition
lastResponse = try await session.respond(to: text).content
}
}
swift
// ✓ Alternatif: custom actor untuk isolasi yang lebih ketat
actor AIProcessor {
private let session: LanguageModelSession
init(instructions: String) {
session = LanguageModelSession(instructions: Instructions(instructions))
}
func process(_ text: String) async throws -> String {
try await session.respond(to: text).content
}
}
// Penggunaan dari main app
let processor = AIProcessor(instructions: "Kamu analisis teks.")
let result = try await processor.process(userInput)
Multiple Sessions Konkuren
Untuk memproses banyak item secara paralel, buat session terpisah per task:
swift
// Proses beberapa dokumen secara paralel — masing-masing dengan session sendiri
func summarizeDocuments(_ documents: [Document]) async throws -> [String] {
try await withThrowingTaskGroup(of: (Int, String).self) { group in
for (index, document) in documents.enumerated() {
group.addTask {
// Setiap task punya session sendiri — tidak ada sharing state
let session = LanguageModelSession()
let summary = try await session.respond(
to: "Rangkum dokumen ini dalam 2 kalimat: \(document.content)"
).content
return (index, summary)
}
}
var results = [String](repeating: "", count: documents.count)
for try await (index, summary) in group {
results[index] = summary
}
return results
}
}
Task Cancellation yang Benar
swift
@Observable
class StreamingViewModel {
var content = ""
var isStreaming = false
private var streamTask: Task<Void, Error>?
private let session = LanguageModelSession()
func startStreaming(prompt: String) {
streamTask?.cancel() // Cancel streaming sebelumnya
content = ""
isStreaming = true
streamTask = Task { @MainActor in
defer { isStreaming = false }
do {
let stream = session.streamResponse(to: prompt)
for try await partial in stream {
try Task.checkCancellation() // Respect cancellation
content = partial.content
}
} catch is CancellationError {
// Dibatalkan oleh user — normal, tidak perlu error handling
} catch {
content = "Error: \(error.localizedDescription)"
}
}
}
func stopStreaming() {
streamTask?.cancel()
streamTask = nil
}
}