Kotlin Academy · درس

‏Mutex وSemaphore للحالة المشتركة

احمِ الحالة المشتركة القابلة للتغيير باستخدام Mutex وحدّد التزامن باستخدام Semaphore.

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

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

مشكلة الحالة المشتركة

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

import kotlinx.coroutines.*
fun main() = runBlocking {
    var counter = 0
    val jobs = List(1000) {
        launch(Dispatchers.Default) { counter++ } // race condition!
    }
    jobs.forEach { it.join() }
    println("Expected 1000, got: $counter") // likely wrong
}

أساسيات Mutex

إن Mutex قفل لمنع التزامن بين coroutine. وهو يعلّق المستدعي، ولا يحجبه، حتى يصبح القفل متاحًا.

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
fun main() = runBlocking {
    val mutex = Mutex()
    var counter = 0
    val jobs = List(1000) {
        launch(Dispatchers.Default) {
            mutex.withLock { counter++ }  // safe
        }
    }
    jobs.forEach { it.join() }
    println("Counter: $counter") // 1000
}

امتداد withLock

يحصل mutex.withLock { ... } على القفل، وينفذ الكتلة، ثم يحرره، حتى عند حدوث استثناء أو إلغاء.

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
fun main() = runBlocking {
    val mutex = Mutex()
    var list = mutableListOf<Int>()
    List(10) {
        launch {
            mutex.withLock {
                list.add(it)
            }
        }
    }.forEach { it.join() }
    println(list.sorted())
}

استخدام lock وunlock يدويًا

يمكنك استدعاء lock() وunlock() مباشرةً لتحكم أدق، لكن يُفضّل استخدام withLock لتجنب نسيان تحرير القفل.

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
fun main() = runBlocking {
    val mutex = Mutex()
    mutex.lock()
    try {
        println("Critical section")
    } finally {
        mutex.unlock()  // must always release
    }
}

Mutex غير قابل لإعادة الدخول

إن Mutex في Kotlin غير قابل لإعادة الدخول. وسيؤدي استدعاء lock() مرة أخرى من coroutine نفسها إلى حالة توقف تام. استخدم عدّادًا أو أعد هيكلة الشيفرة لتجنب الأقفال المتداخلة.

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
fun main() = runBlocking {
    val mutex = Mutex()
    // This deadlocks:
    // mutex.withLock { mutex.withLock { println("never") } }
    mutex.withLock {
        println("Acquired once — safe")
    }
}

أساسيات Semaphore

تسمح Semaphore(n) لما يصل إلى n من coroutine بالمتابعة بشكل متزامن. وهي تشبه نظام تذاكر يحتوي على n خانات.

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
fun main() = runBlocking {
    val semaphore = Semaphore(3) // max 3 concurrent
    List(10) { i ->
        launch {
            semaphore.withPermit {
                println("Concurrent task $i"); delay(100)
            }
        }
    }.forEach { it.join() }
}

امتداد withPermit

يحصل semaphore.withPermit { ... } على تصريح، وينفذ الكتلة، ثم يحرر التصريح؛ وهو المكافئ الآمن لـ coroutine لعملية try-acquire الخاصة بـ semaphore.

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
fun main() = runBlocking {
    val sem = Semaphore(2)
    val results = mutableListOf<String>()
    List(5) { i ->
        launch {
            sem.withPermit {
                delay(50)
                synchronized(results) { results.add("task-$i") }
            }
        }
    }.forEach { it.join() }
    println(results)
}

تحديد معدل الطلبات باستخدام Semaphore

استخدم semaphore لتحديد عدد استدعاءات API المتزامنة، بحيث تسمح بـ N من الطلبات في الوقت نفسه.

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
val apiSemaphore = Semaphore(5) // max 5 concurrent requests
suspend fun callApi(id: Int): String {
    return apiSemaphore.withPermit {
        delay(100) // simulate network
        "Response-$id"
    }
}
fun main() = runBlocking {
    val results = (1..20).map { async { callApi(it) } }.awaitAll()
    println("Got ${results.size} responses")
}

Mutex مقابل Semaphore

Mutex: semaphore ثنائي بتصريح واحد، للوصول الحصري. Semaphore(n): يحتوي على n من التصاريح، للتزامن المحدود. استخدم Mutex لحماية مورد، وSemaphore للحد من التنفيذ المتوازي.

import kotlinx.coroutines.sync.*
// Mutex = Semaphore(1) for exclusive access
// Semaphore(n) = n-way concurrency limit
val exclusive = Mutex()          // one at a time
val limited   = Semaphore(3)     // three at a time
fun main() { println("Mutex for exclusion; Semaphore for rate limiting") }

عداد ذري باستخدام Mutex

نفّذ فئة عدّاد آمنة للخيوط باستخدام Mutex لحماية عمليات الزيادة والقراءة.

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
class AtomicCounter {
    private val mutex = Mutex()
    private var count = 0
    suspend fun increment() = mutex.withLock { count++ }
    suspend fun get() = mutex.withLock { count }
}
fun main() = runBlocking {
    val counter = AtomicCounter()
    List(100) { launch { counter.increment() } }.forEach { it.join() }
    println("Count: ${counter.get()}")
}

بدائل: موزّع أحادي الخيط

بدلًا من استخدام mutex، احصر الحالة القابلة للتغيير في موزّع أحادي الخيط. تعمل coroutine واحدة فقط على ذلك الموزّع في كل مرة، مما يجعل الوصول آمنًا بطبيعته.

import kotlinx.coroutines.*
fun main() = runBlocking {
    val singleThread = newSingleThreadContext("CounterThread")
    var counter = 0
    List(1000) {
        launch(singleThread) { counter++ }
    }.forEach { it.join() }
    println("Counter: $counter") // 1000, no mutex needed
    singleThread.close()
}

تحقق سريع

ما الفرق الأساسي بين Mutex وSemaphore؟

مراجعة

يوفر Mutex وصولًا حصريًا إلى الحالة المشتركة، ويُفضّل استخدام withLock. ويحد Semaphore(n) من التزامن إلى n. ويعلّق كلاهما coroutine المنتظرة بدلًا من حجبها. وفي الحالات البسيطة، يكون الموزّع أحادي الخيط غالبًا أوضح.

البدء مجانًا

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

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

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

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

هل درس «‏Mutex وSemaphore للحالة المشتركة» مجاني؟

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

ماذا ستتعلم في «‏Mutex وSemaphore للحالة المشتركة»؟

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

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

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

كم من الوقت يستغرق درس «‏Mutex وSemaphore للحالة المشتركة»؟

معظم دروس 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