التزامن وتعدد الخيوط في Kotlin: المستوى المتقدم
في التطوير الحديث، تعد الإدارة الفعالة للتزامن مفتاحًا لإنشاء تطبيقات سريعة وسريعة الاستجابة. تقدم Kotlin، بفضل الدعم المدمج للكوروتينات (coroutines)، مجموعة أدوات قوية ومرنة تتجاوز بكثير الخيوط القياسية في Java. في هذه المقالة، سنستعرض المفاهيم المتقدمة للتزامن وتعدد الخيوط في Kotlin، بما في ذلك العمل مع Channel وFlow وMutex ودقائق المجدولين.
1. الكوروتينات: أعمق من الأساسيات
الكوروتينات هي خيوط خفيفة الوزن تسمح بكتابة كود غير متزامن بأسلوب أمري. ومع ذلك، على المستوى المتقدم، من المهم فهم كيفية عمل الموزعين (Dispatchers) وسياقات الكوروتين.
المجدولون (Dispatchers)
اختيار الموزع الصحيح أمر بالغ الأهمية للأداء:
Dispatchers.Default— للمهام المكثفة لوحدة المعالجة المركزية (CPU) (يستخدم افتراضيًا مجموعة خيوط مساوية لعدد أنوية المعالج).Dispatchers.IO— لعمليات الإدخال والإخراج (الشبكة، الملفات). يمكن أن تتوسع مجموعة الخيوط حتى 64 خيطًا (أو أكثر، حسب التكوين).Dispatchers.Main— لخيط واجهة المستخدم (UI) (Android، JavaFX).newSingleThreadContextوnewFixedThreadPoolContext— لإنشاء مجموعات خيوط مخصصة.
import kotlinx.coroutines.*
fun main() = runBlocking { launch(Dispatchers.Default) { println("مهمة مكثفة لوحدة المعالجة على ${Thread.currentThread().name}") } launch(Dispatchers.IO) { println("مهمة إدخال/إخراج على ${Thread.currentThread().name}") }}التزامن المنظم
إحدى الميزات الرئيسية لـ Kotlin هي التزامن المنظم. يضمن ذلك اكتمال الكوروتينات الفرعية قبل اكتمال الكوروتين الأصلي. يمنع هذا تسرب الذاكرة ويبسط إدارة دورة الحياة.
fun main() = runBlocking { coroutineScope { launch { delay(1000) println("المهمة 1 مكتملة") } launch { delay(500) println("المهمة 2 مكتملة") } } println("جميع المهام مكتملة")}2. القنوات (Channel) لنقل البيانات بين الكوروتينات
Channel هو نظير لقائمة الانتظار المحظورة (BlockingQueue)، ولكن للكوروتينات. يسمح بنقل البيانات بأمان بين الكوروتينات، مع تعليق المرسل إذا كانت القناة ممتلئة، أو المستقبل إذا كانت القناة فارغة.
أنواع القنوات
- RendezvousChannel (الافتراضي) — لا يحتوي على مخزن مؤقت. يتم حظر الإرسال حتى الاستلام.
- BufferedChannel — يحتوي على مخزن مؤقت بحجم محدد.
- ConflatedChannel — يخزن فقط آخر قيمة مرسلة، مع استبدال القيم السابقة.
- UnlimitedChannel — مخزن مؤقت غير محدود (تحذير: قد يسبب نفاد الذاكرة OOM).
import kotlinx.coroutines.*import kotlinx.coroutines.channels.*
fun main() = runBlocking { val channel = Channel<Int>(capacity = 5) // قناة مخزنة
launch { for (i in 1..10) { channel.send(i) println("تم الإرسال: $i") } channel.close() }
launch { for (value in channel) { println("تم الاستلام: $value") delay(100) } }}Fan-out و Fan-in
القنوات مناسبة تمامًا لأنماط "الإخراج المروحي" (منتج واحد، عدة مستهلكين) و"الإدخال المروحي" (عدة منتجين، مستهلك واحد).
3. Flow: تدفقات البيانات التفاعلية
على عكس Channel، الذي يعتبر "ساخنًا" (يتم إرسال البيانات بغض النظر عن وجود مشترك)، فإن Flow هو تدفق "بارد" يتم