Jamie Taylor
03/16/2020, 5:13 PMfun main() = runBlocking<Unit> {
val a = newSingleThreadContext("a")
val b = newSingleThreadContext("b")
launch {
val set = mutableSetOf<Int>()
repeat(100000) {
withContext(a) {
set.add(it)
}
withContext(b) {
set.remove(it)
}
}
println(set.size)
}
}
I'd have thought it wouldn't be since set is being modified on multiple threads and isn't thread safe. On the other hand I can't see anything in the docs warning me not to do this and I haven't managed to produce any errors by doing seemingly unsafe operations.Casey Brooks
03/16/2020, 5:19 PMlaunch { }
block is running async, but each withContext()
call is still run sequentially, and each iteration of the repeat
block is also run sequentially. That’s the nature of coroutines, sequentially-async code. You’d have to launch { }
or async { }
each iteration of the loop for it to actually be executed in parallel, and I’d guess you would start seeing concurrent modification errors thenJamie Taylor
03/16/2020, 5:24 PMCasey Brooks
03/16/2020, 5:31 PMmutableSetOf
(LinkedHashSet
) is not thread-safe, and so your snippet is technically not safe. But it doesn’t keep checks of which threads are adding/removing elements from it, and as far as the Set is concerned, there are no threading issues since at one point in time only one thread is doing anything to it (since it’s not running in parallel). Each add/remove operation is synchronous, so it will not get in a point where an add is half-done by the time the next thread goes to remove it, since the Coroutines ensure the two withContext
blocks are run sequentially, not in parallelwithContext
block will always finish to completion before the second one ever starts, unless you launch
or async
it.Deferred
or Job
), you have a guarantee of this. This is actually one of the benefits of using coroutines, being able to use lock-free data structures in these situationsAnimesh Sahu
03/17/2020, 7:10 AMJamie Taylor
03/17/2020, 9:25 AM