TGViewer
Kotlin Kotlin @kotlin_lib · 2.05K subscribers
Post #712 354
🚧 Ваш Mutex тормозит корутины. Как перестать лочить и начать жить

Вы пишете многопоточный код. У вас есть общий ресурс (например, in-memory кэш сессий). Вы знаете, что synchronized в корутинах - это зло (блокирует системный поток).
Вы берете Mutex и оборачиваете код в mutex.withLock { ... }.

Поздравляю, вы сделали лучше. Но под большим contention (высокой конкуренцией) ваш код всё равно будет страдать.

В чем проблема Mutex?
Да, он не блокирует поток. Но withLock - это suspend функция.
Когда 100 корутин ломятся в один лок, 1 получает доступ, а 99 - саспендятся. Саспенд - это не бесплатно. Это сохранение стейт-машины, аллокации Continuation, а потом дорогой диспетчеризированный возврат (resume) каждой корутины к жизни.

🛠 Как от этого уходят Сеньоры? (2 пути)

Путь 1: Lock-free (Для простых стейтов)
Если у вас счетчик или простая ссылка на объект, забудьте про локи. Используйте атомики (CAS-операции — Compare-And-Swap). Они выполняются на уровне железа процессора.


// ❌ Медленно:
val mutex = Mutex()
var count = 0
// ... mutex.withLock { count++ }

// ✅ Летает:
val count = AtomicInteger(0)
// ... count.incrementAndGet()

// 🧠 Для Kotlin-объектов:
val state = MutableStateFlow(InitialState)
// Под капотом использует CAS-цикл без локов!
state.update { it.copy(...) }



Путь 2: Thread Confinement (Для сложной логики)
Если логика внутри лока большая (нужно сходить в мапу, проверить список, обновить 3 переменные) - CAS не спасет.

Вместо того чтобы обносить данные забором из локов, спрячьте данные в один поток.
Вспоминаем наш прошлый пост про limitedParallelism(1) - это официально рекомендуемая JetBrains замена устаревшим Actors.


class SessionManager {
// Создаем свой "однопоточный" диспетчер
private val stateContext = Dispatchers.Default.limitedParallelism(1)

// Переменная вообще без потокобезопасных оберток!
private val cache = mutableMapOf<String, Session>()

suspend fun updateSession(id: String, data: Session) {
// Все операции с кэшем встают в аккуратную очередь
// на выполнение в один поток. Никаких race conditions.
withContext(stateContext) {
val existing = cache[id]
cache[id] = merge(existing, data)
}
}
}



Почему Confinement (ограничение потока) лучше?

1. Вы избавляетесь от Deadlocks по определению.
2. Процессору гораздо проще прогнать очередь инструкций на одном ядре (работает L1/L2 кэш процессора), чем прыгать между потоками, пытающимися захватить Mutex.
3. Код становится линейным и предсказуемым.

Вывод:
Mutex хорош, когда коллизии редки.
Если ресурс "горячий" (в него долбятся постоянно) - изолируйте стейт в StateFlow (если можно) или в отдельный limitedParallelism(1) контекст (если логика сложная).

Признавайтесь, у кого проект усыпан Mutex() на каждом чихе? 👇

📲 Мы в MAX

✍️ @kotlin_lib
  • 🔥 6
  • 👍 3
More from @kotlin_lib
  1. Jul 25, 2026🚀 Подборка полезных IT каналов в Max Системное администрирование, DevOps 📌 https://max.r…
  2. Jul 1, 2026🌪 Ваша дата в опасности: flatMapConcat vs Merge vs Latest Вам нужно взять поток ID-шников…
  3. Jun 24, 2026🤖 Android-приложение — это не только красивый экран. За ним стоят работа с внешним API, з…
  4. Jun 23, 2026🔮 Убийца бойлерплейта: Встречайте Context Parameters (Kotlin 2.x) Мы так привыкли к Depen…
  5. Jun 3, 2026Яндекс обновил Yandex Mobile Ads SDK для монетизации мобильных приложений — и это интересн…
  6. Jun 3, 2026ktorgen - Kotlin + KSP + Ktor Client Code Generator Легкий процессор Kotlin KSP для генера…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →