Swift parallellik (concurrency)
Swift parallellik (concurrency)
async/await, Task’lar, actor’lar va kooperativ bekor qilish yordamida xavfsiz, tuzilmaviy asinxron kod yozing.
Asosiy GCD (Grand Central Dispatch)
Ishni asinxron bajarish uchun DispatchQueue dan foydalaning.
Quyidagi misol vazifani fon navbatida (background queue) bajaradi va uning tugashini kutadi.
Sintaksis:
DispatchQueue.global().async { ... }DispatchGroup.enter()DispatchGroup.leave()DispatchGroup.wait()
Misol
import Dispatch
print("Start")
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
print("Background work")
group.leave()
}
group.wait()
print("Done")
Ushbu misol ishni fon navbatida bajaradi va DispatchGroup yordamida tugashini kutadi.
Maslahat: Zamonaviy Swift’da tuzilmaviy parallellik uchun GCD o‘rniga async/await va Task ni afzal ko‘ring.
Task bilan async/await
async/await asinxron kodni sinxron kodga o‘xshab ko‘rinadigan tarzda yozish imkonini beradi.
Sinxron kontekstlardan parallel ishni boshlash uchun Task dan foydalaning.
Sintaksis:
func name() async -> T {}await valueTask { ... }
Misol
import Dispatch
func fetchValue() async -> Int { 7 }
print("Start")
let sem = DispatchSemaphore(value: 0)
Task {
let v = await fetchValue()
print("Got \(v)")
sem.signal()
}
sem.wait()
print("Done")
Ushbu misol Task yordamida asinxron ishni boshlaydi va async/await orqali qiymatni kutadi.
async let (parallel ichki vazifalar)
Bir nechta ichki vazifani (child task) parallel ishga tushirish va ularning natijalarini kutish uchun async let dan foydalaning.
Sintaksis: async let name = expression() ichki vazifani ishga tushiradi; qiymatni o‘qishda await dan foydalaning.
Misol
import Dispatch
func fetch(_ id: Int) async -> Int { id * 10 }
print("Start")
let sem = DispatchSemaphore(value: 0)
Task {
async let a = fetch(1)
async let b = fetch(2)
let total = await (a + b)
print("Total \(total)")
sem.signal()
}
sem.wait()
print("Done")
Ushbu misol ikkita ichki vazifani parallel ishga tushiradi va jami qiymatni hisoblash uchun ikkalasini ham kutadi.
Async/await va xatolar
Asinxron funksiyalar ham xato tashlashi mumkin.
try ni await bilan birga ishlating va muvaffaqiyatsizliklarni do/catch bilan qayta ishlang.
Sintaksis:
func name() async throws -> Ttry awaitdo { ... } catch { ... }
Misol
import Dispatch
enum FetchError: Error { case bad }
func fetch(_ ok: Bool) async throws -> Int {
if !ok { throw FetchError.bad }
return 42
}
print("Start")
let sem = DispatchSemaphore(value: 0)
Task {
do {
let v = try await fetch(false)
print("ok \(v)")
} catch {
print("error")
}
sem.signal()
}
sem.wait()
print("Done")
Ushbu misolda asinxron funksiya atrofida try/await va do/catch yordamida xatolarni qayta ishlash ko‘rsatilgan.
Task guruhlari (task groups)
Parallel ichki vazifalarni tarqatish va ularning natijalarini jamlash uchun withTaskGroup dan foydalaning.
Sintaksis: withTaskGroup(of: ReturnType.self) { group in ... } vazifalarni qo‘shadi va natijalar bo‘ylab iteratsiya qiladi.
Misol
import Dispatch
func square(_ n: Int) async -> Int { n * n }
print("Start")
let sem = DispatchSemaphore(value: 0)
Task {
var results: [Int] = []
await withTaskGroup(of: Int.self) { group in
for n in [1,2,3] {
group.addTask { await square(n) }
}
for await val in group {
results.append(val)
}
}
print(results.sorted().map(String.init).joined(separator: ","))
sem.signal()
}
sem.wait()
print("Done")
Ushbu misol sonlar ro‘yxati uchun kvadratlarni parallel hisoblaydi va natijalarni task group’dan jamlaydi.
Actor’lar va MainActor
Actor’lar o‘z o‘zgaruvchan holatini murojaatlarni ketma-ketlashtirish orqali ma’lumotlar poygasidan (data race) himoya qiladi.
Asosiy oqimda (main thread) bajarilishi shart bo‘lgan kodni (masalan, UI yangilanishlarini) belgilash uchun @MainActor dan foydalaning.
Sintaksis: actor Name { ... } actor e’lon qiladi; tashqaridan metod/xususiyatlarni await bilan chaqiring.
MainActor: Asosiy oqimda bajarilishi uchun tiplar/funksiyalarni @MainActor bilan belgilang.
Misol
import Dispatch
actor SafeCounter {
private var value = 0
func increment() { value += 1 }
func get() -> Int { value }
}
let counter = SafeCounter()
print("Start")
let sem = DispatchSemaphore(value: 0)
Task {
await withTaskGroup(of: Void.self) { group in
for _ in 0..<1000 {
group.addTask { await counter.increment() }
}
}
print("Final: \(await counter.get())")
sem.signal()
}
sem.wait()
print("Done")
Ushbu misol o‘zgaruvchan holatni himoya qilish uchun actor’dan foydalanadi va oshirishlarni task group orqali jamlaydi.
Maslahat: UI bilan bog‘liq API’lar asosiy oqimda bajarilishini ta’minlash uchun ularni @MainActor bilan belgilang.
Vazifani bekor qilish (task cancellation)
Uzoq davom etadigan ishni task.cancel() ni chaqirish orqali bekor qiling va bekor qilinganligini Task.isCancelled yoki try Task.checkCancellation() bilan tekshiring.
Sintaksis:
t.cancel()Task.isCancelledtry Task.checkCancellation()
Misol
import Dispatch
func slowWork() async throws {
for i in 1...5 {
try await Task.sleep(nanoseconds: 300_000_000) // 0.3s
try Task.checkCancellation()
print("Step ", i)
}
}
let sem = DispatchSemaphore(value: 0)
let t = Task {
do { try await slowWork() } catch { print("Cancelled") }
sem.signal()
}
DispatchQueue.global().asyncAfter(deadline: .now() + 0.7) {
t.cancel()
}
sem.wait()
Ushbu misolda ishlayotgan vazifani bekor qilish va bekor qilinganligini tekshirish orqali kooperativ bekor qilish ko‘rsatilgan.
W3Schools Pathfinder
Yutuqlaringizni kuzating – bu bepul!
