Замок в Kotlin
У Mutex есть пара методов lock и
unlock. Первый приостанавливает корутину,
пока замок занят, второй отдает его следующему
ожидающему. Между ними лежит критический участок.
Если исключение вылетит внутри участка, замок всё
равно нужно снять. Парой try и finally
unlock вызывают даже при ошибке:
import kotlinx.coroutines.sync.Mutex
suspend fun addTicket(gate: Mutex, counter: IntArray) {
gate.lock()
try {
counter[0]++
} finally {
gate.unlock()
}
}
На практике счетчик хранят снаружи, а под замком
меняют его поле. Две корутины крутят приращение
билетов и после joinAll сверяют сумму:
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
fun main() {
runBlocking {
var tickets = 0
val gate = Mutex()
val jobs = List(2) {
launch {
repeat(500) {
gate.lock()
try {
tickets++
} finally {
gate.unlock()
}
}
}
}
joinAll(*jobs.toTypedArray())
println(tickets)
}
}
Тот же каркас подходит, когда между приращениями
есть пауза: замок удерживается только на время
изменения, а delay выполняется уже снаружи:
import kotlinx.coroutines.delay
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
suspend fun bump(gate: Mutex, slot: IntArray) {
delay(10)
gate.lock()
try {
slot[0]++
} finally {
gate.unlock()
}
}
fun main() {
runBlocking {
val gate = Mutex()
val slot = intArrayOf(0)
val jobs = listOf(
launch { bump(gate, slot) },
launch { bump(gate, slot) },
)
joinAll(*jobs.toTypedArray())
println(slot[0])
}
}
Общий Mutex и переменная coins
типа Int. Две фоновые корутины по
300 раз увеличивают монеты под
ручным замком с finally. После
ожидания обеих выведите итог.
Счетчик visits и один замок. Три
корутины делают короткую паузу, затем под
замком добавляют единицу. Дождитесь всех
и напечатайте число визитов.