Kotlin Academy · درس

Actors وإدارة الحالة المنظّمة

استخدم أنماطًا شبيهة بـ actor لتسلسل الوصول إلى الحالة في الشيفرة المتزامنة.

الدرس 4 من 413 خطوة

Actors وإدارة الحالة المنظّمة درس مجاني في Kotlin Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Kotlin Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Kotlin Academy 4 دروس في المجموع.

ما هو Actor؟

إن actor هو coroutine يمتلك حالة خاصة قابلة للتغيير ويتواصل عبر قناة. ولا يصل الكود الخارجي إلى الحالة مباشرةً، بل يتعامل معها من خلال الرسائل فقط.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
sealed class CounterMsg
object Increment : CounterMsg()
class GetCount(val response: CompletableDeferred<Int>) : CounterMsg()
fun CoroutineScope.counterActor() = actor<CounterMsg> {
    var counter = 0
    for (msg in channel) {
        when (msg) {
            is Increment -> counter++
            is GetCount  -> msg.response.complete(counter)
        }
    }
}

استخدام Actor

أرسل الرسائل إلى actor باستخدام send. ويعالج actor الرسائل واحدة تلو الأخرى، مما يجعل الوصول إلى الحالة تسلسليًا.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
// (CounterMsg sealed class from previous scene)
fun main() = runBlocking {
    val counter = counterActor()
    repeat(100) { counter.send(Increment) }
    val response = CompletableDeferred<Int>()
    counter.send(GetCount(response))
    println("Count: ${response.await()}") // 100
    counter.close()
}

Actor بديلًا عن Mutex

تلغي Actors الحاجة إلى mutexes بجعل الوصول إلى الحالة تسلسليًا حسب التصميم. وتحدث جميع عمليات التغيير داخل coroutine واحدة.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
sealed class Msg
object Inc : Msg()
data class Get(val d: CompletableDeferred<Int>) : Msg()
fun CoroutineScope.safeCounter() = actor<Msg> {
    var n = 0
    for (m in channel) when(m) {
        is Inc -> n++
        is Get -> m.d.complete(n)
    }
}
fun main() = runBlocking {
    val a = safeCounter()
    repeat(1000) { a.send(Inc) }
    val d = CompletableDeferred<Int>()
    a.send(Get(d))
    println(d.await()) // 1000
    a.close()
}

بروتوكول رسائل محدد الأنواع

عرّف بروتوكول الرسائل الخاص بك على هيئة تسلسل هرمي لفئة sealed، لضمان معالجة جميع الحالات في تعبير when داخل actor.

sealed class BankMsg
data class Deposit(val amount: Double) : BankMsg()
data class Withdraw(val amount: Double, val result: CompletableDeferred<Boolean>) : BankMsg()
data class Balance(val result: CompletableDeferred<Double>) : BankMsg()
// Actor holds balance privately — outside code sends messages only

آلة الحالة بصفتها Actor

تُعد Actors آلات حالة طبيعية، إذ تحدث انتقالات الحالة الداخلية بشكل ذري استجابةً للرسائل.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
enum class TrafficState { RED, GREEN, YELLOW }
fun CoroutineScope.trafficLight() = actor<Unit> {
    var state = TrafficState.RED
    for (msg in channel) {
        state = when (state) {
            TrafficState.RED    -> TrafficState.GREEN
            TrafficState.GREEN  -> TrafficState.YELLOW
            TrafficState.YELLOW -> TrafficState.RED
        }
        println("State: $state")
    }
}
fun main() = runBlocking {
    val light = trafficLight()
    repeat(6) { light.send(Unit) }
    light.close()
}

منشئ actor()

ينشئ منشئ coroutine ‏actor { } كائن actor مع صندوق وارد من نوع channel. وactor هو coroutine يعالج الرسائل الواردة من channel.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun CoroutineScope.logActor() = actor<String>(capacity = Channel.BUFFERED) {
    val log = mutableListOf<String>()
    for (msg in channel) {
        log.add(msg)
        println("[LOG] $msg")
    }
    println("Log entries: ${log.size}")
}
fun main() = runBlocking {
    val logger = logActor()
    repeat(5) { logger.send("Event $it") }
    logger.close()
    // actor finishes after close
}

إيقاف actor

أغلق channel الإرسال الخاص بـactor باستخدام close(). تنتهي حلقة for داخل actor، مما يتيح تنفيذ التنظيف قبل انتهاء coroutine.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun main() = runBlocking {
    val actor = actor<String> {
        for (msg in channel) println("Got: $msg")
        println("Actor done")
    }
    actor.send("hello")
    actor.send("world")
    actor.close()
    // Wait for actor to finish
    delay(50)
}

Actors مع backpressure

اضبط سعة channel الخاص بـactor لتطبيق backpressure، بحيث تتوقف عمليات الإرسال مؤقتًا عندما يمتلئ صندوق الوارد.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
fun CoroutineScope.slowProcessor() = actor<Int>(capacity = 2) {
    for (item in channel) {
        delay(100) // slow processing
        println("Processed: $item")
    }
}
fun main() = runBlocking {
    val proc = slowProcessor()
    repeat(5) { proc.send(it) } // sender suspends when capacity full
    proc.close()
}

أداء actor مقارنةً بـ Mutex

تعمل actors على تسلسل الوصول من دون تنافس على الأقفال، ولذلك تناسب الحالات المعقدة التي تتضمن أنواعًا متعددة من الرسائل. أما Mutex فهو أبسط عند التعامل مع عدّاد مشترك واحد، لكنه أقل قابلية للتركيب.

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
// For a simple counter: Mutex is fine
val mutex = Mutex()
var simpleCounter = 0
// For complex state + multiple operations: Actor is cleaner
// Actor: messages describe intent; state changes are encapsulated
fun main() = runBlocking { println("Choose based on state complexity") }

CompletableDeferred لنمط الطلب والرد

في أنماط الطلب والرد داخل actor، أدرج CompletableDeferred في الرسالة. يكمله actor، ثم ينتظر المرسل نتيجته.

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
data class Query(val key: String, val reply: CompletableDeferred<String?>)
fun CoroutineScope.cacheActor() = actor<Any> {
    val cache = mutableMapOf<String, String>()
    for (msg in channel) when (msg) {
        is Pair<*, *> -> cache[msg.first as String] = msg.second as String
        is Query -> msg.reply.complete(cache[msg.key])
    }
}

البديل الحديث: StateFlow + coroutineScope

منشئ actor تجريبي وقد تتم إزالته لاحقًا. والبديل الحديث هو استخدام ViewModel أو فئة خدمة مع MutableStateFlow يتم تحديثه من coroutine واحدة.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
class CounterService(scope: CoroutineScope) {
    private val _count = MutableStateFlow(0)
    val count = _count.asStateFlow()
    private val events = kotlinx.coroutines.channels.Channel<Unit>()
    init {
        scope.launch {
            for (e in events) _count.value++
        }
    }
    fun increment() { events.trySend(Unit) }
}

تحقق سريع

كيف تضمن actors الوصول الآمن إلى الحالة المتزامنة؟

مراجعة

تغلف actors الحالة القابلة للتغيير داخل coroutine واحدة، وتعرض العمليات على شكل رسائل مكتوبة النوع عبر channel. وهي تفرض تسلسل الوصول تلقائيًا، وتدعم آلات الحالات، وتزيل التنافس على الأقفال، وذلك على حساب طبقة إضافية لتمرير الرسائل.

البدء مجانًا

تعلم Kotlin مع معلم ذكاء اصطناعي — مجانًا

اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.

الدورات
51
الدروس
203

الأسئلة الشائعة

هل درس «Actors وإدارة الحالة المنظّمة» مجاني؟

نعم — نص درس «Actors وإدارة الحالة المنظّمة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Kotlin Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Kotlin Academy 4 دروس في المجموع.

ماذا ستتعلم في «Actors وإدارة الحالة المنظّمة»؟

استخدم أنماطًا شبيهة بـ actor لتسلسل الوصول إلى الحالة في الشيفرة المتزامنة. تتمرن على Kotlin Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Kotlin Academy؟

لا تُشترط خبرة سابقة. Kotlin Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «Actors وإدارة الحالة المنظّمة»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Kotlin Academy هذا؟

نعم. كل درس في Kotlin Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. أساسيات Channel: ‏send وreceive وclose
  2. أنواع Channel: ‏Rendezvous وBuffered وConflated وUnlimited
  3. ‏Mutex وSemaphore للحالة المشتركة
  4. Actors وإدارة الحالة المنظّمة
← العودة إلى Kotlin Academy