线程安全LiveData更新



我有以下具有竞争条件的代码。我尝试在列表中找到一个项目并设置其加载属性。但是如果从不同的线程多次调用onLoaded("A")onLoaded("B")。如果第一次调用在第二次开始之前没有完成,我总是会丢失第一次调用的数据。

我怎样才能使它工作?使用互斥应该是正确的方法吗?

val list = MutableLiveData<List<Model>>() // assume this is initialized with ["Model(false, "A"), Model(false, "B")]
data class Model(
val loaded: Boolean,
val item: String,
)
fun onLoaded(item: String) = viewModelScope.launch {
val currList = list.value ?: return@launch
withContext(Dispatchers.Default) {
val updated = currList.find { it.item == item }?.copy(loaded = true)
val mutable = currList.toMutableList()
updated?.let {
val index = mutable.indexOf(it)
mutable[index] = it
}
list.postValue(mutable.toList())
}
}
onLoaded("A")
onLoaded("B")
expected: ["Model(true, "A"), Model(true, "B")]
actual: ["Model(false, "A"), Model(true, "B")]

onLoaded()中使用viewModelScope启动了一个新的协程。viewModelScopeDispatchers.Main.immediate上下文,所以它里面的代码将在主线程上执行,例如,执行仅限于一个线程。出现竞态条件的原因是连续调用onLoaded()函数并不能保证协程执行的顺序。

  • 如果你从一个线程中连续调用onLoaded(),我建议删除在其中启动协程viewModelScope.launch。这样就能保持拜访的顺序了。在本例中使用list.postValue()

  • 如果你从不同的线程调用onLoaded(),仍然想要启动一个协程,你可以参考这个问题的答案。

  • 尝试使用@Synchronized注释而不启动协程:

    @Synchronized
    fun onLoaded(item: String) { ... }
    

    方法将由定义该方法的实例的监视器保护,以防止多线程并发执行该方法。在本例中使用list.postValue()

最新更新