I am going through this coroutines
hands-on tutorial Coroutines-Channels. So there is this task of concurrently fetching contributors and showing intermediate progress at the same time using channels
, See Here
Below is snippet of the proposed solution
suspend fun loadContributorsChannels(
service: GitHubService,
req: RequestData,
updateResults: suspend (List<User>, completed: Boolean) -> Unit) = coroutineScope {
........
.........
val channel = Channel<List<User>>()
for (repo in repos) {
launch {
val users = service.getRepoContributors(req.org, repo.name) // suspend function
.also { logUsers(repo, it) }
.bodyList()
channel.send(users) // suspend function
}
}
var allUsers = emptyList<User>()
repeat(repos.size) {
val users = channel.receive() // suspend function
allUsers = (allUsers + users).aggregate()
updateResults(allUsers, it == repos.lastIndex) // suspend function
}
}
The function loadContributorsChannels()
is called inside a coroutine
which is using a Default dispatcher
.See here. I have 2 questions.
In the snippet above is
allUsers
being modified concurrently since we are already inside acoroutine
which is using aDefault dispatcher
?If I change the code sequence like below why do I get incorrect results? How is the code above different from the snippet below?
val contributorsChannel = Channel<List<User>>() var contributors = emptyList<User>() for(repo in repos) { launch { val contributorsPerRepo = service .getRepoContributors(req.org, repo.name) // suspend function .also { logUsers(repo, it) } .bodyList() contributors = (contributors + contributorsPerRepo).aggregate() contributorsChannel.send(contributors) // suspend function } } repeat(repos.size) { updateResults(contributorsChannel.receive(), it == repos.lastIndex) // suspend functions }
Is this because of concurrent modification or am I missing something?
In the original code, the top-level coroutine is the only one using
allUsers
. It is its local state.In your code,
contributors
is a variable shared by all the coroutines and concurrently updated.The original code correctly applies the Channel as a synchronization mechanism to fan-in all the concurrent computation into a single coroutine that collects the results and uses them.