Issue
I have the following code which has a race condition. I try to find an item in a list and set its loading property. But if onLoaded("A")
and onLoaded("B")
are called multiple times from different threads. I always lose the data of the first call if it doesn't complete before second starts.
How can I make this work? Is using Mutex should be the correct approach?
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")]
Solution
In onLoaded()
a new coroutine is launched using viewModelScope
. viewModelScope
has Dispatchers.Main.immediate
context, so the code inside it will be executed on the Main Thread, e.g. execution is limited to only one thread. The reason you have a Race Condition because calling the onLoaded()
function consecutively doesn't guarantee the order of coroutines execution.
If you call
onLoaded()
consecutively from one thread I suggest to remove launching a coroutineviewModelScope.launch
in it. Then the order of calling will be preserved. Uselist.postValue()
in this case.If you call
onLoaded()
from different threads and still want to launch a coroutine you can refer to answers to this question.Try to use
@Synchronized
anotation without launching a coroutine:@Synchronized fun onLoaded(item: String) { ... }
Method will be protected from concurrent execution by multiple threads by the monitor of the instance on which the method is defined. Use
list.postValue()
in this case.
Answered By - BigSt
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.