التزامن وتعدد الخيوط في Swift: مستوى متقدم
التزامن (concurrency) هو أحد أكثر المواضيع تعقيدًا وأهمية في التطوير لنظامي iOS/macOS. على المستوى المتقدم، يقدم Swift أدوات قوية: من Grand Central Dispatch (GCD) الكلاسيكي إلى نموذج async/await الحديث ونظام Actors الصارم. في هذه المقالة، سنشرح كيفية إدارة الخيوط بكفاءة، وتجنب سباقات البيانات، وكتابة كود آمن للخيوط.
1. Grand Central Dispatch (GCD): العمل مع قوائم الانتظار والمجموعات
يظل GCD أساس تعدد الخيوط في Swift. ومع ذلك، على المستوى المتقدم، من المهم فهم ليس فقط الفرق بين main و global()، بل أيضًا القدرة على إدارة تبعيات المهام.
إنشاء قوائم انتظار مخصصة
let serialQueue = DispatchQueue(label: "com.pythonlib.serial")let concurrentQueue = DispatchQueue(label: "com.pythonlib.concurrent", attributes: .concurrent)
serialQueue.async { print("المهمة 1 في قائمة انتظار تسلسلية")}concurrentQueue.async { print("المهمة 2 في قائمة انتظار متوازية")}DispatchGroup: مزامنة عدة مهام
let group = DispatchGroup()let queue = DispatchQueue.global()
queue.async(group: group) { sleep(1) print("تحميل البيانات 1")}queue.async(group: group) { sleep(2) print("تحميل البيانات 2")}
group.notify(queue: DispatchQueue.main) { print("اكتملت جميع التحميلات، نقوم بتحديث واجهة المستخدم")}استخدم DispatchGroup عندما تحتاج إلى انتظار إتمام عدة عمليات متوازية (مثل تحميل الصور أو تحليل JSON).
2. النموذج الحديث async/await و Task
بدءًا من Swift 5.5، حصلت اللغة على دعم أصلي للبرمجة غير المتزامنة من خلال الكلمات المفتاحية async و await. هذا يبسط قراءة الكود ويتخلص من "هرم الإغلاقات".
تعريف واستدعاء دالة غير متزامنة
func fetchUserData() async -> String { // محاكاة طلب شبكة try? await Task.sleep(nanoseconds: 2_000_000_000) return "تم تحميل بيانات المستخدم"}
Task { let result = await fetchUserData() print(result) // سيتم طباعة "تم تحميل بيانات المستخدم"}استدعاءات متوازية باستخدام async let
func loadImage() async -> Data { return Data() }func loadMetadata() async -> String { return "meta" }
Task { async let image = loadImage() async let metadata = loadMetadata() let result = try await (image, metadata) print("الصورة: \\(result.0)، البيانات الوصفية: \\(result.1)")}يتيح async let تشغيل عدة مهام غير متزامنة بالتوازي وانتظارها جميعًا. هذا أكثر كفاءة من الاستدعاء التسلسلي.
3. Actors: أمان الخيوط على مستوى اللغة
الممثلون (Actors) هم آلية جديدة للحماية من سباقات البيانات. يضمنون أن الوصول إلى حالتهم سيكون متزامنًا تلقائيًا.
تعريف واستخدام الممثل
actor BankAccount { private var balance: Double = 0.0 func deposit(amount: Double) { balance += amount print("الرصيد بعد الإيداع: \\(balance)") } func withdraw(amount: Double) -> Bool { guard balance >= amount else { return false } balance -= amount return true } func getBalance() -> Double { balance }}
Task { let account = BankAccount() await account.deposit(amount: 100) let success = await account.withdraw(amount: 50) print("السحب ناجح: \\(success)")}جميع طرق الممثل معزولة افتراضيًا - يمكن استدعاؤها فقط عبر await. هذا يمنع التغيير العشوائي للبيانات من عدة خيوط.
الطرق غير المعزولة (nonisolated)
actor Logger { nonisolated func log(message: String) { print("سجل: \\(message)")\