返回

协程 Mutex 无法阻止并行修改?这篇文章告诉你原因!

Android

协程 Mutex 难以阻止多个协程修改或读取数据

问题

当在协程之间共享重要数据时,使用 Mutex 来防止并发的修改或读取操作至关重要。然而,在某些情况下,即使使用 Mutex,也可能出现并行修改异常。

考虑以下示例:我们有一个火车列表(trains),我们需要在协程之间共享。为了保护该列表,我们在修改方法中使用了 Mutex。然而,在removeUnusedTrains方法中,我们没有使用 Mutex,导致并行修改异常。

原因

并行修改异常发生的原因是:

  • removeUnusedTrains方法在不加锁的情况下修改trains列表。
  • 同时,另一个协程也可能正在访问该列表。
  • removeUnusedTrains方法尝试修改列表时,它可能会与另一个协程并行操作,从而导致异常。

解决方法

解决此问题的关键是始终在修改trains列表时获取mutex锁。这将确保一次只有一个协程可以修改列表,从而避免并发修改异常。

修改后的代码如下:

class TrainService {
    private var trains : MutableList<Train> = emptyList<Train>().toMutableList()

    private val mutex = Mutex()

    suspend fun addTrain(newTrain: MatOfPoint, trainColor: Scalar) = mutex.withLock {
        for(train in trains) {
            when {
                train.isSame(newTrain) -> return true
            }
        }
        val train = Train(newTrain, trainColor)
        trains.add(train)
    }

    suspend fun removeUnusedTrains() = mutex.withLock {
        val iterator = trains.iterator()
        while (iterator.hasNext()) {
            val train = iterator.next()
            if (!train.changed) {
                iterator.remove()
            } else {
                train.changed = false
            }
        }
        Log.d("TrainService", "Trains now: ${trains.joinToString()}")
    }
}

常见问题解答

1. Mutex 是处理协程并发修改的唯一方法吗?

不,还有其他方法可以处理协程并发修改,例如使用原子变量或不可变数据结构。

2. 即使使用 Mutex,为什么仍然可能出现并发修改异常?

如果在获取 Mutex 锁之前对数据进行了修改,则可能会出现并发修改异常。

3. 如何避免并行修改异常?

始终在修改共享数据时获取 Mutex 锁。

4. Mutex 会影响协程性能吗?

是的,Mutex 会引入一些开销,但它对于防止并发修改异常至关重要。

5. 何时使用 Mutex?

当多个协程需要访问和修改共享数据时,应使用 Mutex。