Sobes.tech
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.