# Swift Structured Concurrency Deep Dive: TaskGroup, AsyncStream ve Daha Fazlası
Swift'in concurrency modeli sadece async/await'ten ibaret değil. Structured Concurrency, paralel görevlerin yaşam döngüsünü, iptalini ve hata yönetimini deterministik hale getiren güçlü bir paradigma. Bu rehberde, TaskGroup'tan AsyncStream'e, Continuation'lardan priority yönetimine kadar structured concurrency'nin tüm derinliklerini keşfedeceğiz.
Ön koşul: Bu rehber, async/await temellerini bildiğinizi varsayar. Temel bilgilere ihtiyaç duyarsanız "Async/Await Best Practices" yazımıza göz atın.
İçindekiler
- Structured vs Unstructured Concurrency
- TaskGroup Derinlemesine
- AsyncStream ve AsyncSequence
- Continuation Patterns
- Cancellation Yönetimi
- Priority ve Fairness
- Karşılaştırma Tablosu
- ALTIN İPUCU
- Sonuç ve Öneriler
1. Structured vs Unstructured Concurrency
Structured Concurrency'de her child task, parent task'ın scope'u içinde yaşar. Parent bittiğinde tüm child'lar da biter. Bu, resource leak'leri önler ve reasoning'i kolaylaştırır.
Temel Farklar
Özellik | Structured | Unstructured (Task) | Detached (Task.detached) |
|---|---|---|---|
**Parent ilişkisi** | Var | Var (priority, actor) | Yok |
**Cancellation** | Parent ile otomatik | Manuel | Manuel |
**Priority** | Parent'tan miras | Parent'tan miras | Bağımsız |
**Actor context** | Parent'tan miras | Parent'tan miras | Bağımsız |
**Lifetime** | Parent scope ile sınırlı | Bağımsız | Bağımsız |
**Hata propagation** | Otomatik | Manuel | Manuel |
**Kullanım** | Paralel iş bölümü | Fire-and-forget | Background iş |
Structured Concurrency Kuralı
swift
1// STRUCTURED: Child task'lar parent scope icinde2func fetchDashboard() async throws -> Dashboard {3 // async let ile paralel calisma4 async let profile = fetchProfile()5 async let notifications = fetchNotifications()6 async let feed = fetchFeed()7 8 // Tum sonuclar hazir olana kadar bekle9 // Herhangi biri hata firlatirsa, digerlerini iptal et10 return try await Dashboard(11 profile: profile,12 notifications: notifications,13 feed: feed14 )15}16// Fonksiyon bittiginde tum child task'lar kesinlikle bitmis olur17 18// UNSTRUCTURED: Task bagimsiz yasar19func startBackgroundSync() {20 Task {21 // Bu task, cagiran fonksiyondan bagimsiz yasiyor22 // Kim cancel edecek? Kim hatayi handle edecek?23 await syncAllData()24 }25}2. TaskGroup Derinlemesine
TaskGroup, dinamik sayıda paralel görev oluşturmanızı sağlar. async let'ten farkı: görev sayısını runtime'da belirleyebilirsiniz.
Temel Kullanım
swift
1// MARK: - Paralel Image Download2func downloadImages(urls: [URL]) async throws -> [UIImage] {3 try await withThrowingTaskGroup(of: (Int, UIImage).self) { group in4 // Her URL icin bir task olustur5 for (index, url) in urls.enumerated() {6 group.addTask {7 let (data, _) = try await URLSession.shared.data(from: url)8 guard let image = UIImage(data: data) else {9 throw ImageError.invalidData10 }11 return (index, image)12 }13 }14 15 // Sonuclari topla (sirasiz gelir!)16 var results: [(Int, UIImage)] = []17 for try await result in group {18 results.append(result)19 }20 21 // Orijinal siraya gore duz22 return results23 .sorted { $0.0 < $1.0 }24 .map { $0.1 }25 }26}Throttled TaskGroup (Concurrency Limiti)
swift
1// MARK: - Throttled Paralel Islem2func processItems<T, R>(3 _ items: [T],4 maxConcurrency: Int = 4,5 operation: @Sendable @escaping(T) async throws -> R6) async throws -> [R] {7 try await withThrowingTaskGroup(of: (Int, R).self) { group in8 var results: [(Int, R)] = []9 var nextIndex = 010 11 // Ilk batch'i ekle12 for _ in 0..<min(maxConcurrency, items.count) {13 let index = nextIndex14 group.addTask {15 let result = try await operation(items[index])16 return (index, result)17 }18 nextIndex += 119 }20 21 // Biri bitince bir sonrakini ekle (sliding window)22 for try await result in group {23 results.append(result)24 25 if nextIndex < items.count {26 let index = nextIndex27 group.addTask {28 let result = try await operation(items[index])29 return (index, result)30 }31 nextIndex += 132 }33 }34 35 return results.sorted { $0.0 < $1.0 }.map { $0.1 }36 }37}38 39// Kullanim40// let processedImages = try await processItems(41// imageURLs,42// maxConcurrency: 343// ) { url in44// try await downloadAndResize(url)45// }Easter Egg
Gizli bir bilgi buldun!
Bu bölümde gizli bir bilgi var. Keşfetmek ister misin?
3. AsyncStream ve AsyncSequence
AsyncStream, push-based kaynaklardan (delegate, callback, NotificationCenter) pull-based async sequence oluşturur.
AsyncStream Oluşturma
swift
1// MARK: - Location Stream2extension CLLocationManager {3 var locationStream: AsyncStream<CLLocation> {4 AsyncStream { continuation in5 let delegate = LocationDelegate(continuation: continuation)6 self.delegate = delegate7 8 continuation.onTermination = { _ in9 // Temizlik10 self.stopUpdatingLocation()11 }12 13 self.startUpdatingLocation()14 }15 }16}17 18// Delegate -> AsyncStream koprusu19private final class LocationDelegate: NSObject, CLLocationManagerDelegate {20 let continuation: AsyncStream<CLLocation>.Continuation21 22 init(continuation: AsyncStream<CLLocation>.Continuation) {23 self.continuation = continuation24 }25 26 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {27 for location in locations {28 continuation.yield(location)29 }30 }31 32 func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {33 continuation.finish()34 }35}36 37// Kullanim38// for await location in locationManager.locationStream {39// updateMap(with: location)40// }AsyncThrowingStream ile Hata Yönetimi
swift
1// MARK: - WebSocket Stream2func createWebSocketStream(url: URL) -> AsyncThrowingStream<WebSocketMessage, Error> {3 AsyncThrowingStream { continuation in4 let task = URLSession.shared.webSocketTask(with: url)5 6 func receiveNext() {7 task.receive { result in8 switch result {9 case .success(let message):10 switch message {11 case .string(let text):12 continuation.yield(.text(text))13 case .data(let data):14 continuation.yield(.binary(data))15 @unknown default:16 break17 }18 receiveNext() // Sonraki mesaji bekle19 20 case .failure(let error):21 continuation.finish(throwing: error)22 }23 }24 }25 26 continuation.onTermination = { _ in27 task.cancel(with: .goingAway, reason: nil)28 }29 30 task.resume()31 receiveNext()32 }33}34 35enum WebSocketMessage {36 case text(String)37 case binary(Data)38}4. Continuation Patterns
Continuation, callback-based API'ları async/await dünyasına köprüler.
swift
1// MARK: - Continuation ile Callback Bridge2func requestCameraPermission() async -> Bool {3 await withCheckedContinuation { continuation in4 AVCaptureDevice.requestAccess(for: .video) { granted in5 continuation.resume(returning: granted)6 }7 }8}9 10// Throwing version11func fetchFromLegacySDK(id: String) async throws -> LegacyResult {12 try await withCheckedThrowingContinuation { continuation in13 LegacySDK.fetch(id: id) { result, error in14 if let error = error {15 continuation.resume(throwing: error)16 } else if let result = result {17 continuation.resume(returning: result)18 } else {19 continuation.resume(throwing: LegacyError.unknown)20 }21 }22 }23 // DIKKAT: Continuation MUTLAKA tam olarak bir kez resume edilmeli!24 // Sifir kez = memory leak, iki kez = crash25}5. Cancellation Yönetimi
Structured Concurrency'nin en güçlü yanlarından biri otomatik cancellation propagation'dır.
swift
1// MARK: - Cancellation-Aware Code2func processLargeDataset(_ items: [DataItem]) async throws -> [ProcessedItem] {3 var results: [ProcessedItem] = []4 5 for item in items {6 // Her iterasyonda iptal kontrolu7 try Task.checkCancellation()8 9 let processed = try await process(item)10 results.append(processed)11 12 // Alternatif: cooperative cancellation13 if Task.isCancelled {14 // Temizlik yap ve partial sonuc don15 await cleanup()16 return results // Partial result17 }18 }19 20 return results21}22 23// withTaskCancellationHandler24func downloadFile(url: URL) async throws -> Data {25 let sessionTask = URLSession.shared.dataTask(with: url)26 27 return try await withTaskCancellationHandler {28 try await withCheckedThrowingContinuation { continuation in29 sessionTask.completionHandler = { data, response, error in30 if let error = error {31 continuation.resume(throwing: error)32 } else if let data = data {33 continuation.resume(returning: data)34 }35 }36 sessionTask.resume()37 }38 } onCancel: {39 sessionTask.cancel() // Task iptal edilirse network istegini de iptal et40 }41}6. Priority ve Fairness
Task priority, sistem kaynaklarının nasıl dağıtılacağını belirler.
Priority | Kullanım | Örnek |
|---|---|---|
**.userInitiated** | Kullanıcının beklediği işlemler | Butona tıklama sonucu |
**.medium** | Varsayılan, genel amaçlı | Veri senkronizasyonu |
**.utility** | Uzun süren, UI bloklamayan | Büyük dosya indirme |
**.background** | Kullanıcı farkında olmayan | Analytics gönderimi |
**.low** | En düşük öncelik | Prefetch, cache ısıtma |
**.high** | Yüksek öncelik | Gerçek zamanlı güncelleme |
swift
1// MARK: - Timeout destekli TaskGroup wrapper2func withTimeout<T: Sendable>(3 seconds: TimeInterval,4 operation: @Sendable @escaping() async throws -> T5) async throws -> T {6 try await withThrowingTaskGroup(of: T.self) { group in7 // Ana islem8 group.addTask { try await operation() }9 10 // Watchdog timer11 group.addTask {12 try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))13 throw TimeoutError(duration: seconds)14 }15 16 // Ilk biten kazanir, digerini iptal et17 guard let result = try await group.next() else {18 throw TimeoutError(duration: seconds)19 }20 group.cancelAll()21 return result22 }23}24 25struct TimeoutError: Error {26 let duration: TimeInterval27 var localizedDescription: String { "Islem \(duration)sn icinde tamamlanamadi" }28}29 30// Kullanim: let data = try await withTimeout(seconds: 15) { try await fetchData() }ALTIN İPUCU
Bu yazının en değerli bilgisi
Bu ipucu, yazının en önemli çıkarımını içeriyor.
Okuyucu Ödülü
Tebrikler! Bu yazıyı sonuna kadar okuduğun için sana özel bir hediyem var:
Sonuç ve Öneriler
Swift Structured Concurrency, concurrent programlamanın geleceğidir. TaskGroup ile dinamik paralellik, AsyncStream ile reactive pattern'ler, Continuation ile legacy köprüsü kurun. Cancellation'ı her zaman düşünün ve priority'leri doğru atayın. Structured concurrency tercih edin, unstructured Task'ı sadece gerçekten gerektiğinde kullanın.

