Swift parallellik (concurrency)


ULASHISH

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")

O‘zingiz sinab ko‘ring »

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 value
  • Task { ... }

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")

O‘zingiz sinab ko‘ring »

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")

O‘zingiz sinab ko‘ring »

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 -> T
  • try await
  • do { ... } 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")

O‘zingiz sinab ko‘ring »

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")

O‘zingiz sinab ko‘ring »

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")

O‘zingiz sinab ko‘ring »

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.isCancelled
  • try 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()

O‘zingiz sinab ko‘ring »

Ushbu misolda ishlayotgan vazifani bekor qilish va bekor qilinganligini tekshirish orqali kooperativ bekor qilish ko‘rsatilgan.



W3Schools Pathfinder

Yutuqlaringizni kuzating – bu bepul!