التزامن وتعدد الخيوط في Kotlin: المستوى المتقدم
\nفي التطوير الحديث باستخدام Kotlin، خاصة لتطبيقات الخادم (Server) وتطبيقات Android، فإن الإدارة الجيدة للتزامن هي مفتاح الأداء والاستجابة. على المستوى المتقدم، لا يكفي مجرد تشغيل coroutine. بل يجب فهم كيفية مزامنة الوصول إلى البيانات بكفاءة، وإدارة الخيوط، وتجنب "الجحيم" من الأقفال. في هذه المقالة، سنشرح التقنيات المتقدمة: من تفاصيل kotlinx.coroutines إلى المجموعات الآمنة للخيوط والقنوات.
1. الغوص العميق في coroutines: التزامن المنظم (Structured Concurrency) والنطاقات (Scopes)
\nأساس العمل المتقدم هو التزامن المنظم (Structured Concurrency). وهو مبدأ يتم فيه ربط دورة حياة coroutine بنطاق الرؤية (scope). إذا تم إلغاء coroutine الأصلي، يتم إنهاء جميع المهام التابعة له تلقائيًا. هذا يمنع تسرب الذاكرة والعمليات غير المنضبطة.
\nعلى المستوى المتقدم، يجب عليك إنشاء نطاقات خاصة بك، بدلاً من الاعتماد على النطاق العام GlobalScope.
import kotlinx.coroutines.*\nimport kotlin.coroutines.CoroutineContext\n\nclass DataRepository {\n // نطاق خاص مع موزع ومعالج استثناءات\n private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler { _, throwable ->\n println("خطأ غير معالج: ${throwable.message}")\n })\n\n fun loadData() {\n scope.launch {\n // coroutines تابعة\n val data = async { fetchFromNetwork() }\n val cache = async { loadFromCache() }\n process(data.await(), cache.await())\n }\n }\n\n fun cleanup() {\n scope.cancel() // إلغاء جميع coroutines في النطاق\n }\n}\n\nاستخدم SupervisorJob عندما لا يجب أن يؤدي فشل coroutine تابع إلى إلغاء البقية. هذا أمر بالغ الأهمية لطبقات واجهة المستخدم (UI) أو الطلبات المستقلة المتوازية.
2. المجموعات الآمنة للخيوط (Thread-Safe Collections) والعمليات الذرية (Atomic Operations)
\nحتى مع coroutines، إذا كنت تعمل مع بيانات قابلة للتغيير ومشتركة، فأنت بحاجة إلى المزامنة. يقدم Kotlin عدة طرق:
\n- \n
- الأنواع الذرية (
AtomicInteger,AtomicReference) — للعدادات والأعلام البسيطة. \n - المجموعات الآمنة للخيوط (
ConcurrentHashMap,CopyOnWriteArrayList) — للهياكل المعقدة. \n - Mutex من
kotlinx.coroutines— للأقسام الحرجة داخل coroutines. \n
import kotlinx.coroutines.*\nimport kotlinx.coroutines.sync.Mutex\nimport kotlinx.coroutines.sync.withLock\nimport java.util.concurrent.ConcurrentHashMap\nimport java.util.concurrent.atomic.AtomicInteger\n\nclass SafeCounter {\n private val mutex = Mutex()\n private var counter = 0\n\n suspend fun increment() {\n mutex.withLock {\n counter++ // قسم حرج\n }\n }\n\n fun getCounter(): Int = counter\n}\n\nclass CacheManager {\n private val cache = ConcurrentHashMap<String, String>()\n private val hitCount = AtomicInteger(0)\n\n fun put(key: String, value: String) {\n cache[key] = value\n }\n\n fun get(key: String): String? {\n val result = cache[key]\n if (result != null) hitCount.incrementAndGet()\n return result\n }\n}\n\nهام: Mutex هي دالة تعليق (suspend function)، لذا فهي لا تحجب الخيط، بل تعلق coroutine. هذا أكثر كفاءة من synchronized.
3. القنوات (Channels) و Flow: تدفقات البيانات التفاعلية
\nلنقل البيانات بين coroutines، استخدم Channels (مشابه لـ BlockingQueue لـ coroutines) و Flow (تدفق غير متزامن بارد).
القنوات: منتج-مستهلك (Producer-Consumer)
\nimport kotlinx.coroutines.channels.*\nimport kotlinx.coroutines.*\n\nfun Corout