Mutex และ Semaphore สำหรับสถานะที่ใช้ร่วมกัน
ปกป้องสถานะที่เปลี่ยนแปลงได้และใช้ร่วมกันด้วย Mutex พร้อมจำกัดการทำงานพร้อมกันด้วย Semaphore
Mutex และ Semaphore สำหรับสถานะที่ใช้ร่วมกัน เป็นบทเรียน Kotlin Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Kotlin Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน
ปัญหาสถานะที่ใช้ร่วมกัน
โครูทีนบนหลายเธรดอาจทำให้สถานะที่เปลี่ยนแปลงได้และใช้ร่วมกันเสียหาย หากไม่มีการประสานงาน การเพิ่มค่าพร้อมกันอาจให้ผลลัพธ์ที่ไม่ถูกต้อง
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 เป็นล็อกแบบกีดกันร่วมกันสำหรับโครูทีน โดยจะพักการทำงานแทนการบล็อกผู้เรียกจนกว่าล็อกจะพร้อมใช้งาน
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 ไม่เป็นแบบ Reentrant
Mutex ของ Kotlin ไม่เป็นแบบ reentrant การเรียก lock() อีกครั้งจากโครูทีนเดิมจะทำให้เกิดการหยุดชะงัก ใช้ตัวนับหรือปรับโครงสร้างใหม่เพื่อหลีกเลี่ยงการล็อกซ้อนกัน
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 ตัว เปรียบเสมือนระบบบัตรที่มีช่องว่าง 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 { ... } จะขอสิทธิ์ใช้งาน เรียกใช้บล็อก แล้วปล่อยสิทธิ์ เป็นรูปแบบที่ปลอดภัยสำหรับโครูทีนและเทียบเท่ากับการลองขอสิทธิ์จากเซมาฟอร์
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
ใช้เซมาฟอร์เพื่อจำกัดจำนวนการเรียก 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: เซมาฟอร์แบบไบนารีที่มีสิทธิ์ 1 รายการ สำหรับการเข้าถึงแบบผูกขาด 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 ให้จำกัดสถานะที่เปลี่ยนแปลงได้ไว้กับตัวจัดส่งงานแบบเธรดเดียว ในแต่ละครั้งจะมีโครูทีนเพียงหนึ่งตัวที่ทำงานบนตัวจัดส่งงานนั้น ทำให้การเข้าถึงปลอดภัยโดยธรรมชาติ
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 ทั้งสองแบบจะพักการทำงานของโครูทีนที่รอ แทนการบล็อก ในกรณีง่าย ๆ ตัวจัดส่งงานแบบเธรดเดียวมักมีโครงสร้างที่ชัดเจนกว่า
คำถามที่พบบ่อย
บทเรียน “Mutex และ Semaphore สำหรับสถานะที่ใช้ร่วมกัน” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “Mutex และ Semaphore สำหรับสถานะที่ใช้ร่วมกัน” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Kotlin Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “Mutex และ Semaphore สำหรับสถานะที่ใช้ร่วมกัน”
ปกป้องสถานะที่เปลี่ยนแปลงได้และใช้ร่วมกันด้วย Mutex พร้อมจำกัดการทำงานพร้อมกันด้วย Semaphore คุณปฏิบัติ Kotlin Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Kotlin Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Kotlin Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “Mutex และ Semaphore สำหรับสถานะที่ใช้ร่วมกัน” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Kotlin Academy นี้ได้ไหม
ได้ บทเรียน Kotlin Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- พื้นฐาน Channel: send, receive และ close
- ชนิดของ Channel: Rendezvous, Buffered, Conflated, Unlimited
- Mutex และ Semaphore สำหรับสถานะที่ใช้ร่วมกัน
- Actor และการจัดการสถานะแบบมีโครงสร้าง