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
    }
}