هم روندی (Swift Concurrency)
«هم روندی سویفت (Swift Concurrency)» یعنی کارهای هم زمان را امن و منظم اجرا کنیم. ابزارهای اصلی «async/await»، «Task»، «actor» و لغو همکارانه هستند. مثل مدرسه، چند کار را موازی می بری جلو، اما با نظم و صف.
مرور سریع هم روندی سویفت
با async/await کد غیرهمگام، شبیه کد معمولی می شود. با Task از محیط همگام، کار هم روند شروع کن. بازی طور فکر کن؛ چند مأموریت موازی داری اما نتیجه ها مدیریت می شود.
GCD پایه با DispatchQueue
در GCD، کارها را در صف پس زمینه اجرا می کنی. سپس با DispatchGroup منتظر اتمام می مانی. این روش قدیمی تر است، اما فهم سازوکار صف ها را ساده می کند.
import Dispatch
print("Start")
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
print("Background work")
group.leave()
}
group.wait()
print("Done")
نکته: در سویفت مدرن، معمولاً async/await و Task را ترجیح بده؛ ساختارمندتر است.
async/await و Task
با async/await نوشتن ساده تر می شود. از Task برای شروع کار هم روند استفاده کن و سپس با await نتیجه را بگیر.
import Dispatch
func fetchValue() async -> Int {
return 7
}
print("Start")
let sem = DispatchSemaphore(value: 0)
Task {
let v = await fetchValue()
print("Got \(v)")
sem.signal()
}
sem.wait()
print("Done")
async let برای کارهای موازی
با async let چند کارِ فرزند را هم زمان شروع کن. سپس با await نتیجه همه را بگیر.
import Dispatch
func fetch(_ id: Int) async -> Int {
return 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")
async/await همراه خطا
توابع async می توانند throw کنند. با try await فراخوانی کن و در do/catch خطا را بگیر. برای مبانی، صفحه مدیریت خطا را ببین.
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")
گروه های تسک (Task Groups)
با withTaskGroup کارها را پخش کن و نتایج را جمع کن. مثل تیم پروژه؛ هر نفر کاری می آورد و تو مرتب می کنی.
import Dispatch
func square(_ n: Int) async -> Int {
return n * n
}
print("Start")
let sem = DispatchSemaphore(value: 0)
Task {
var results: [Int] = []
await withTaskGroup(of: Int.self) { group in {
}
}
}
هشدار: نتایج را هنگام پیمایش گروه جمع کن و سپس مصرف کن. ترتیب تضمین نمی شود.
Actors و MainActor
«actor» از مسابقه داده جلوگیری می کند. دسترسی ها را صف می کند. برای کدهای UI از @MainActor استفاده کن تا روی نخ اصلی اجرا شود.
import Dispatch
actor SafeCounter {
private var value = 0
func increment() {
value += 1
}
func get() -> Int {
return 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")
نکته: اگر State به UI مرتبط است، آن API را با @MainActor علامت بزن.
لغو تسک ها (Cancellation)
کارِ طولانی را می توان لغو کرد. با t.cancel() لغو کن و با Task.isCancelled یا Task.checkCancellation() بررسی کن.
import Dispatch
func slowWork() async throws {
for i in 1...5 {
try await Task.sleep(nanoseconds: 300_000_000)
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()
گام های عملی
- نمونه ها را اجرا کن و خروجی را ببین.
- مقادیر را عوض کن و زمان بندی را بررسی کن.
- برای خطاها، به مدیریت خطا برگرد.
برای آشنایی با حافظه در کنار هم روندی، صفحه مدیریت حافظه را ببین. همچنین از لینک هم روندی سویفت به عنوان مرجع ثابت استفاده کن.
جمع بندی سریع
- async/await کد را ساده و خوانا می کند.
- Task کار هم روند را آغاز می کند.
- async let کارهای موازی می سازد.
- actor از مسابقه داده جلوگیری می کند.
- لغو، همکاری کار و برنامه را حفظ می کند.