التزامن وتعدد الخيوط في Swift: المستوى المتقدم
\n\nتتطلب التطبيقات الحديثة لنظامي iOS وmacOS أداءً عاليًا واستجابة سريعة للواجهة. يتجاوز العمل المتقدم مع التزامن في Swift بكثير مجرد استخدام Grand Central Dispatch (GCD). في هذه المقالة، سنستعرض الأدوات الرئيسية في Swift 5.5+ لإدارة المهام غير المتزامنة: async/await، Actors، TaskGroup وبروتوكول Sendable. ستتعلم كيفية تجنب سباقات البيانات، وهيكلة الكود متعدد الخيوط بشكل صحيح، وتحسين الأداء.
1. أساسيات async/await والتزامن المنظم (Structured Concurrency)
\n\nالكلمات المفتاحية async و await هي أساس التزامن الحديث في Swift. فهي تسمح بكتابة كود غير متزامن يبدو وكأنه متزامن، مما يبسط القراءة والتصحيح بشكل كبير.
إعلان دالة غير متزامنة:
\nfunc fetchUserData() async throws -> User {\n let url = URL(string: "https://api.example.com/user")!\n let (data, _) = try await URLSession.shared.data(from: url)\n let decoder = JSONDecoder()\n return try decoder.decode(User.self, from: data)\n}\n\nاستدعاء دالة غير متزامنة مع معالجة الأخطاء:
\nTask {\n do {\n let user = try await fetchUserData()\n print("User name: \\(user.name)")\n } catch {\n print("Failed to fetch user: \\(error)")\n }\n}\n\nنقاط مهمة:
\n- \n
- التزامن المنظم (Structured Concurrency): كل مهمة غير متزامنة لها عمر محدد بوضوح، محدود بنطاق رؤية المهمة الأصلية
Task. \n - إلغاء المهام: إلغاء تلقائي للمهام الفرعية عند إلغاء المهمة الأصلية Task. \n
- الأولويات: استخدم
Task(priority: .userInitiated)للمهام المهمة للمستخدم. \n
2. Actors: الحماية من سباقات البيانات
\n\nقبل Swift 5.5، كان المطورون غالبًا ما يستخدمون قوائم انتظار GCD المتسلسلة لحماية الحالة المشتركة. توفر Actors عزلًا مدمجًا: الوصول إلى حالة الـ actor ممكن فقط من خلال await، مما يضمن سلامة الخيوط على مستوى المترجم.
مثال على Actor لحساب بنكي:
\nactor BankAccount {\n private var balance: Double = 0.0\n\n func deposit(amount: Double) {\n balance += amount\n }\n\n func withdraw(amount: Double) throws {\n guard balance >= amount else {\n throw BankError.insufficientFunds\n }\n balance -= amount\n }\n\n func getBalance() -> Double {\n return balance\n }\n}\n\n// الاستخدام\nlet account = BankAccount()\nTask {\n await account.deposit(amount: 100.0)\n let currentBalance = await account.getBalance()\n print("Balance: \\(currentBalance)")\n}\n\nمزايا Actors:
\n- \n
- فحص المترجم: محاولة الوصول إلى خاصية الـ actor بدون
awaitستؤدي إلى خطأ في الترجمة. \n - إعادة الدخول (Reentrancy): يمكن للـ actor تعليق تنفيذ الطريقة الحالية (مثلاً عند استخدام
awaitداخل الـ actor) ومعالجة استدعاء آخر، مما يمنع حالات الجمود (deadlocks). \n - الأداء: تستخدم Actors آليات مزامنة محسّنة، وغالبًا ما تكون أسرع من الأقفال اليدوية. \n
3. TaskGroup: المعالجة المتوازية للبيانات
\n\nعند الحاجة لتنفيذ عدة عمليات غير متزامنة مستقلة وانتظار اكتمالها، فإن TaskGroup هي الأداة المثالية. فهي تدير تلقائيًا عدد المهام المتوازية وتجمع النتائج.
مثال لتحميل عدة صور بالتوازي:
\nfunc downloadImages(urls: [URL])