Mutex i Semaphore dla współdzielonego stanu
Chroń współdzielony mutowalny stan za pomocą Mutex i ograniczaj współbieżność przy użyciu Semaphore.
Mutex i Semaphore dla współdzielonego stanu to bezpłatna lekcja Kotlin Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Kotlin Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Kotlin Academy zawiera 4 lekcji w sumie.
Problem współdzielonego stanu
Korutyny działające na wielu wątkach mogą uszkodzić współdzielony, mutowalny stan. Bez synchronizacji współbieżne zwiększanie wartości prowadzi do nieprawidłowych wyników.
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
}Podstawy Mutex
Mutex to blokada wzajemnego wykluczania dla korutyn. Zawiesza wykonanie wywołującego (zamiast je blokować), dopóki blokada nie będzie dostępna.
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
}Rozszerzenie withLock
mutex.withLock { ... } przejmuje blokadę, wykonuje blok i zwalnia blokadę — także w przypadku wyjątku lub anulowania.
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())
}Ręczne lock / unlock
Można bezpośrednio wywoływać lock() i unlock(), aby uzyskać większą kontrolę, ale należy preferować withLock, aby uniknąć pominięcia odblokowania.
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 nie jest reentrantny
W Kotlinie Mutex NIE jest reentrantny. Ponowne wywołanie lock() z tej samej korutyny spowoduje zakleszczenie. Należy użyć licznika albo zmienić strukturę kodu, aby uniknąć zagnieżdżonego blokowania.
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")
}
}Podstawy Semaphore
Semaphore(n) pozwala maksymalnie n korutom działać współbieżnie. Przypomina system biletowy z n miejscami.
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() }
}Rozszerzenie withPermit
semaphore.withPermit { ... } rezerwuje pozwolenie, wykonuje blok i zwalnia pozwolenie — jest bezpiecznym dla korutyn odpowiednikiem próby przejęcia pozwolenia semafora.
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)
}Ograniczanie częstotliwości za pomocą Semaphore
Należy użyć semafora do ograniczenia liczby współbieżnych wywołań API — na przykład do N żądań jednocześnie.
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 a Semaphore
Mutex: semafor binarny (1 pozwolenie), zapewniający wyłączny dostęp. Semaphore(n): n pozwoleń, ograniczających współbieżność. Należy używać Mutex do ochrony zasobu, a Semaphore do ograniczania równoległości.
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") }Atomowy licznik z Mutex
Należy zaimplementować bezpieczną wątkowo klasę licznika, używając Mutex do ochrony operacji zwiększania wartości i odczytu.
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()}")
}Alternatywa: dyspozytor jednowątkowy
Zamiast mutexu można ograniczyć mutowalny stan do dyspozytora jednowątkowego. Na tym dyspozytorze w danym momencie działa tylko jedna korutyna, dzięki czemu dostęp jest z natury bezpieczny.
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()
}Szybkie sprawdzenie
Jaka jest główna różnica między Mutex a Semaphore?
Podsumowanie
Mutex zapewnia wyłączny dostęp do współdzielonego stanu — należy preferować withLock. Semaphore(n) ogranicza współbieżność do n korutyn. Oba mechanizmy zawieszają oczekujące korutyny, zamiast je blokować. W prostych przypadkach dyspozytor jednowątkowy często jest czytelniejszym rozwiązaniem.
Często zadawane pytania
Czy lekcja „Mutex i Semaphore dla współdzielonego stanu” jest bezpłatna?
Tak — pełny tekst „Mutex i Semaphore dla współdzielonego stanu” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Kotlin Academy, przejdź na CoddyKit PRO. Kurs Kotlin Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Mutex i Semaphore dla współdzielonego stanu”?
Chroń współdzielony mutowalny stan za pomocą Mutex i ograniczaj współbieżność przy użyciu Semaphore. Ćwiczysz Kotlin Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Kotlin Academy?
Nie wymagamy żadnego doświadczenia. Kotlin Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.
Ile czasu zajmuje lekcja „Mutex i Semaphore dla współdzielonego stanu”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Kotlin Academy?
Tak. Każda lekcja Kotlin Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Podstawy Channel: send, receive i close
- Typy Channel: Rendezvous, Buffered, Conflated, Unlimited
- Mutex i Semaphore dla współdzielonego stanu
- Aktory i strukturalne zarządzanie stanem