Middle+
Защо не се препоръчва използването на блокове за синхронизация и подобни конструкции при работа с корутини?
sobes.tech AI
Отговор от AI
Използването на блокиращи конструкции като synchronized или lock от стандартната библиотека при работа с корутини води до неефективно използване на ресурсите и може да предизвика проблеми.
Основните причини:
- Блокиране на нишката: Синхронизиращите блокове спират изпълнението на нишката, а не само на корутините. Ако вътре в синхронизирания блок се изпълнява Suspending-функция, нишката ще бъде блокирана, докато Suspending-функцията не приключи и блокировката не бъде освободена. Това може да доведе до "глад" на други корутини, използващи същата нишка.
- Несъвместимост със Suspending-функции: Механизмът за работа с корутини (превключване на контекста, продължения) не взема предвид блокировките на ниво нишки. Изпълнението на Suspending-функция вътре в синхронизирания блок нарушава неблокиращата природа на корутините.
- Нарушаване на принципите на структурирана конкуренция: Корутините имат своя система за маркиране на отменяне и йерархия. Блокировките на ниво нишки игнорират тази система, усложнявайки правилната обработка на отмените и управлението на жизнения цикъл.
- Риск от deadlock-и: Комбинацията от блокировки на нишки и механизми за конкуренция на корутините увеличава вероятността за възникване на сложни deadlock-и, които са трудни за отстраняване.
Вместо блокировки на ниво нишки за синхронизиране на достъпа до споделени ресурси в корутините трябва да се използват специализирани примитиви за конкуренция от библиотеката kotlinx.coroutines:
Mutex: Осигурява взаимно изключване. Корутината, която се опитва да захване зает mutex, спира (без блокиране на нишката) докато mutex не бъде освободен.Semaphore: Ограничва броя на корутините, които имат достъп до ресурса едновременно.Channel: Използва се за обмен на данни между корутините.Atomic variables: За прости атомарни операции.
Пример за използване на Mutex:
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.*
val mutex = Mutex()
var counter = 0
suspend fun incrementCounter() {
mutex.withLock {
// Критична секция
counter++
}
}
fun main() = runBlocking {
// Създаваме множество корутини за конкурентен достъп
repeat(1000) {
launch {
incrementCounter()
}
}
delay(1000) // Даваме време на корутините да се изпълнят
println("Counter: $counter") // Очакваме 1000
}
mutex.withLock е suspending-функция. Ако mutex е зает, корутината, която го е извикала, се спира и освобождава нишката за други задачи. Когато mutex стане достъпен, корутината се възобновява. Това не блокира нишката, за разлика от synchronized.