مقدمة: لماذا نحتاج إلى قوائم انتظار المهام في Kotlin؟
\nتتطلب التطبيقات الحديثة بشكل متزايد معالجة متوازية للبيانات، وعمليات خلفية، واستدعاءات غير متزامنة. بدون إدارة ذكية للمهام، قد يواجه كودك مشاكل في الأداء، أو تسربًا للذاكرة، أو "تجميد" الواجهة. قوائم انتظار المهام في Kotlin ليست مجرد تجريد، بل هي أداة قوية تساعد في تنظيم تنفيذ العمل بالترتيب الصحيح، مع التحكم في عدد الخيوط والأولويات.
\nفي هذه المقالة، سنستعرض الأساليب العملية لتنفيذ قوائم انتظار المهام في Kotlin: من ThreadPoolExecutor الكلاسيكي إلى coroutines مع موزعيها المرنين. سترى كيفية تطبيق هذه التقنيات في المشاريع الحقيقية، وستحصل على أمثلة جاهزة للكود.
1. النهج الكلاسيكي: ThreadPoolExecutor و BlockingQueue
\nتوفر المكتبة القياسية لجافا (وكذلك Kotlin كلغة JVM) فئة قوية ThreadPoolExecutor. وهي تدير مجموعة من الخيوط وتستخدم قائمة انتظار داخلية محظورة لتخزين المهام المنتظرة.
إنشاء قائمة انتظار بعدد ثابت من الخيوط
\nأبسط حالة هي مجموعة بعدد ثابت من الخيوط. يتم وضع جميع المهام التي لا يمكن تنفيذها فورًا في قائمة انتظار LinkedBlockingQueue.
import java.util.concurrent.Executors\nimport java.util.concurrent.TimeUnit\n\nfun main() {\n val executor = Executors.newFixedThreadPool(3)\n\n for (i in 1..10) {\n executor.submit {\n println("المهمة $i يتم تنفيذها في الخيط ${Thread.currentThread().name}")\n Thread.sleep(1000)\n }\n }\n\n executor.shutdown()\n executor.awaitTermination(1, TimeUnit.MINUTES)\n println("جميع المهام اكتملت")\n}\n\nيوضح هذا الكود قائمة انتظار أساسية: يتم إرسال المهام إلى المجموعة، وإذا كانت جميع الخيوط مشغولة، فإنها تنتظر في قائمة الانتظار.
\n\nقائمة انتظار ذات أولوية: PriorityBlockingQueue
\nفي بعض الأحيان، يلزم تنفيذ المهام العاجلة قبل البقية. لهذا نستخدم PriorityBlockingQueue مع مقارن مخصص.
import java.util.concurrent.PriorityBlockingQueue\nimport java.util.concurrent.ThreadPoolExecutor\nimport java.util.concurrent.TimeUnit\n\ndata class PriorityTask(val priority: Int, val name: String) : Comparable<PriorityTask> {\n override fun compareTo(other: PriorityTask): Int = other.priority - this.priority\n}\n\nfun main() {\n val queue = PriorityBlockingQueue<Runnable>(10) { r1, r2 ->\n val t1 = (r1 as PriorityTaskWrapper).task\n val t2 = (r2 as PriorityTaskWrapper).task\n t1.compareTo(t2)\n }\n\n val executor = ThreadPoolExecutor(2, 4, 1, TimeUnit.SECONDS, queue)\n\n class PriorityTaskWrapper(val task: PriorityTask, val action: () -> Unit) : Runnable {\n override fun run() = action()\n }\n\n for (i in 1..5) {\n val priority = if (i % 2 == 0) 10 else 1\n executor.submit(PriorityTaskWrapper(PriorityTask(priority, "المهمة $i")) {\n println("جارٍ التنفيذ: ${PriorityTask(priority, "المهمة $i")}")\n Thread.sleep(500)\n })\n }\n\n executor.shutdown()\n executor.awaitTermination(1, TimeUnit.MINUTES)\n}\n\nهنا، سيتم تنفيذ المهام ذات priority = 10 قبل تلك ذات priority = 1، حتى لو تمت إضافتها لاحقًا.
2. قوائم انتظار المهام باستخدام coroutines (kotlinx.coroutines)
\nتقدم Kotlin الحديثة coroutines - وهي خيوط خفيفة الوزن تسمح ببناء قوائم انتظار غير متزامنة دون إدارة مباشرة للخيوط. الأداة الرئيسية هي Channel و Dispatchers.
قائمة انتظار بسيطة عبر Channel
\nChannel هو نظير لقائمة الانتظار المحظورة، ولكن لـ coroutines. يسمح بنقل المهام بأمان بين coroutines.
import kotlinx.coroutines.*\nimport kotlinx.coroutines.channels.Channel\n\nsuspend fun processTas