Sobes.tech
Middle+

How does a mutex work in coroutines?

sobes.tech AI

Answer from AI

A Mutex in coroutines provides a mutual exclusion mechanism to protect shared resources from concurrent access by multiple coroutines.

Working principle:

  1. Lock acquisition: A coroutine that wants to access a protected resource calls the lock() method.
  2. Locking: If the mutex is free, the coroutine non-blockingly acquires it and continues execution. If the mutex is occupied by another coroutine, the calling coroutine suspends until the mutex is released.
  3. Releasing the mutex: After finishing with the protected resource, the coroutine calls the unlock() method to release the mutex.

Key features in coroutines:

  • Non-blocking lock() (when acquiring a free mutex): Unlike traditional threads where lock() can block the entire thread, in coroutines, if the mutex is free, acquisition occurs non-blockingly.
  • Coroutine suspension (when the mutex is occupied): If the mutex is busy, the calling coroutine does not block the thread but suspends until it is released. This frees the thread for executing other coroutines.
  • Extension function withLock: A convenient way to use a mutex that automatically acquires and releases it, even if an exception occurs inside the block.

Usage example:

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.Mutex

val mutex = Mutex()
var counter = 0

suspend fun incrementCounter() {
    mutex.withLock {
        // Critical section - protected resource
        counter++
        delay(10) // Simulate work
        println("Counter: $counter")
    } // Mutex is automatically released here
}

fun main() = runBlocking {
    repeat(100) {
        launch {
            incrementCounter()
        }
    }
    delay(1000)
}

In this example, incrementCounter is a critical section that modifies the shared resource counter. The mutex ensures that only one coroutine accesses this section at a time, preventing race conditions. Using withLock simplifies mutex management.