Kotlin Coroutines Dispatchers.IO is not creating expected threads - android

I was just checking the behaviour of the Coroutine Dispatchers. So I was trying to understand how many threads each of these Dispatchers can create. To check this, I created a loop statement counting from 1 to 1_000_000.
In each loop iteration, I'm creating a coroutine with Dispatchers.IO, But when I see the output, it is not created 1_000_000 coroutines, and all these are DefaultDispatcher-worker threads, and it always stops execution in the range 40000-50000 randomly.
But when I replaced Dispatchers.IO with Dispatchers.Unconfined, it actually created all the 1_000_000 coroutines without fail and all these are created on Main thread.
So I need some help here to understand why Dispatchers.IO is failed in this case.
Dispatchers.IO:
fun main() {
println("Start of main")
val handler = CoroutineExceptionHandler { _, e -> e.printStackTrace() }
runBlocking {
for (i in 1..1000000) {
CoroutineScope(Dispatchers.IO + CoroutineName("Dispatchers.IO")).launch(handler) {
println("${Thread.currentThread().name} task number is :$i")
}
}
}
println("End of main")
}
Dispatchers.Unconfined
fun main() {
println("Start of main")
val handler = CoroutineExceptionHandler { _, e ->
e.printStackTrace()
}
runBlocking {
for (i in 1..1000000) {
CoroutineScope(Dispatchers.Unconfined + CoroutineName("Dispatchers.Unconfined")).launch(handler) {
println("${Thread.currentThread().name} task number is :$i")
}
}
}
println("End of main")
}
Thanks in advance

But when I see the output, it is not created 1_000_000 coroutines, and
all these are DefaultDispatcher-worker threads, and it always stops
execution in the range 40000-50000 randomly.
The reason for this, I believe is that you're not waiting for all child coroutines to complete, and your program exits prematurely.
In the below code, Job instances are collected in a list named jobs. After the loop, jobs.joinAll() causes the execution to wait for all child coroutines to complete.
What is interesting, is the difference in execution time Dispatchers.IO vs. Dispatchers.Unconfined.
Dispatchers.IO: Elapsed time 4.040274590s
Dispatchers.Unconfined: Elapsed time 959.173375ms
When adding the suspending delay(1) to the loop, Dispatchers.Unconfined will change thread from main to kotlinx.coroutines.DefaultExecutor.
Dispatchers.Unconfined executes in strict sequence 1..1_000_000.
Coroutines basics
Tested on AMD 8-core, Ubuntu 20.04.
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.time.ExperimentalTime
import kotlin.time.measureTime
#OptIn(ExperimentalTime::class)
fun main() {
println("Start of main")
measureTime {
runBlocking {
val jobs = mutableListOf<Job>()
repeat(1_000_000) { index ->
jobs.add(launch(Dispatchers.IO) {
println("${Thread.currentThread().name} task number is :${index + 1}")
})
}
/** wait until child coroutines complete */
jobs.joinAll()
println("End of main")
}
}.also { duration -> println("Elapsed time: $duration") }
}

Related

What is the difference Job.Cancel vs Scope.Cancel in coroutines?

I have been exploring coroutines and I am quite surprised to see that cancel called on Job does not actually cancel the job while cancel called on scope cancels the coroutines launched from the scope immediately. Can anyone explain this?
Job cancel example.
/**
* This never stops
*/
#Test
fun coroutineTest7() = runBlocking {
val job = launch(Dispatchers.IO) {
var count = 0
while(true){
if(isActive){
println("Hello ${count++}")
}
else {
println("Job Cancelled but this will still get printed")
}
Thread.sleep(500)
}
}
Thread.sleep(6000)
job.cancel()
Unit
}
Scope cancel example
/**
* This gets cancelled after six seconds
*/
#Test
fun coroutineTest5() = runBlocking {
val newScope = CoroutineScope(Dispatchers.IO)
newScope.launch(Dispatchers.IO) {
var count = 0
while(true){
Thread.sleep(500)
println("Hello ${count++}")
}
}
Thread.sleep(6000)
newScope.cancel()
Unit
}
In your first code block, your Job does not cooperate with cancellation. It has an infinite loop and does not call any suspend functions that cooperate with cancellation, so there is no way to exit the loop or for the Job to be cancelled. Since runBlocking runs for as long as its child coroutines are running, it waits forever for the Job to be complete.
In your second code block, your Job still doesn't cooperate with cancellation, but since it's launched in a different CoroutineScope instead of runBlocking's scope, runBlocking doesn't wait for the Job to finish. So although your Job is running forever, runBlocking will promptly return after the newScope.cancel() call, thereby freeing your app process to exit and cutting the misbehaving coroutine short. I suspect if you had other long-running tests in this Unit test suite, you may continue to see that job logging during other tests until the process exits.
You can modify the second test to continue waiting after the runBlocking to see that the Job is still continuing to run in the background:
#Test
fun coroutineTest5() {
runBlocking {
val newScope = CoroutineScope(Dispatchers.IO)
newScope.launch(Dispatchers.IO) {
var count = 0
while(true){
Thread.sleep(500)
println("Hello ${count++}")
}
}
Thread.sleep(6000)
newScope.cancel()
println("Cancelled scope")
Thread.sleep(2000)
}
println("Run blocking returned")
Thread.sleep(2000)
println("Returning from test")
}

problem with kotlin coroutine runBlocking and stateIn

import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.runBlocking
val numbers: StateFlow<Int> = (1..100).asFlow()
.onEach { delay(100) }
.let {
runBlocking {
it.stateIn(this)
}
}
fun main(){
println("hello")
println(numbers.value)
println("bye")
}
I expect that main function finish only in 100ms (wait for first emission)
but second print happens when all items emitted (takes about 100*100 ms) and also prints last item instead of first one!
am I missing something or it is a bug ?
That’s expected behaviour when you use runBlocking, because runBlocking won’t return until all of its child jobs have completed.
In other words, the numbers property won’t return a value until the entire flow has terminated. This is because calling stateIn(scope) launches a job inside the scope that collects all the items from the flow. You can see where this happens in the source code.
If you want to allow the numbers flow to emit values concurrently with your main function, you'll need to use a scope in which both functions can run together. For example, you could call runBlocking in the main function, and pass the scope as a receiver for the numbers property:
val CoroutineScope.numbers: StateFlow<Int> get() = (1..100).asFlow()
.onEach { delay(100) }
.let {
it.stateIn(this)
}
fun main() = runBlocking {
println("hello")
println(numbers.value)
println("bye")
}

How to run Kotlin coroutines sequentially?

I have an instance of CoroutineScope and log() function which look like the following:
private val scope = CoroutineScope(Dispatchers.IO)
fun log(message: String) = scope.launch { // launching a coroutine
println("$message")
TimeUnit.MILLISECONDS.sleep(100) // some blocking operation
}
And I use this test code to launch coroutines:
repeat(5) { item ->
log("Log $item")
}
The log() function can be called from any place, in any Thread, but not from a coroutine.
After a couple of tests I can see not sequential result like the following:
Log 0
Log 2
Log 4
Log 1
Log 3
There can be different order of printed logs. If I understand correctly the execution of coroutines doesn't guarantee to be sequential. What it means is that a coroutine for item 2 can be launched before the coroutine for item 0.
I want that coroutines were launched sequentially for each item and "some blocking operation" would execute sequentially, to always achieve next logs:
Log 0
Log 1
Log 2
Log 3
Log 4
Is there a way to make launching coroutines sequential? Or maybe there are other ways to achieve what I want?
Thanks in advance for any help!
One possible strategy is to use a Channel to join the launched jobs in order. You need to launch the jobs lazily so they don't start until join is called on them. trySend always succeeds when the Channel has unlimited capacity. You need to use trySend so it can be called from outside a coroutine.
private val lazyJobChannel = Channel<Job>(capacity = Channel.UNLIMITED).apply {
scope.launch {
consumeEach { it.join() }
}
}
fun log(message: String) {
lazyJobChannel.trySend(
scope.launch(start = CoroutineStart.LAZY) {
println("$message")
TimeUnit.MILLISECONDS.sleep(100) // some blocking operation
}
)
}
Since Flows are sequential we can use MutableSharedFlow to collect and handle data sequentially:
class Info {
// make sure replay(in case some jobs were emitted before sharedFlow is being collected and could be lost)
// and extraBufferCapacity are large enough to handle all the jobs.
// In case some jobs are lost try to increase either of the values.
private val sharedFlow = MutableSharedFlow<String>(replay = 10, extraBufferCapacity = 10)
private val scope = CoroutineScope(Dispatchers.IO)
init {
sharedFlow.onEach { message ->
println("$message")
TimeUnit.MILLISECONDS.sleep(100) // some blocking or suspend operation
}.launchIn(scope)
}
fun log(message: String) {
sharedFlow.tryEmit(message)
}
}
fun test() {
val info = Info()
repeat(10) { item ->
info.log("Log $item")
}
}
It always prints the logs in the correct order:
Log 0
Log 1
Log 2
...
Log 9
It works for all cases, but need to be sure there are enough elements set to replay and extraBufferCapacity parameters of MutableSharedFlow to handle all items.
Another approach is
Using Dispatchers.IO.limitedParallelism(1) as a context for the CoroutineScope. It makes coroutines run sequentially if they don't contain calls to suspend functions and launched from the same Thread, e.g. Main Thread. So this solution works only with blocking (not suspend) operation inside launch coroutine builder:
private val scope = CoroutineScope(Dispatchers.IO.limitedParallelism(1))
fun log(message: String) = scope.launch { // launching a coroutine from the same Thread, e.g. Main Thread
println("$message")
TimeUnit.MILLISECONDS.sleep(100) // only blocking operation, not `suspend` operation
}
It turns out that the single thread dispatcher is a FIFO executor. So limiting the CoroutineScope execution to one thread solves the problem.

Kotlin coroutine in lifecycleScope doesn't block main thread

I am confused about coroutines in ViewModels.
My question is pretty simple: why does it seem like the following coroutine doesn't block UIThread? (UI is still smooth while coroutine is running)
My fragment right here:
class FragmentSeePaths : Fragment(R.layout.fragment_see_paths),
PathRecyclerAdapter.OnSetPathForWidgetListener {
private val pathViewModel: PathViewModel by activityViewModels()
private lateinit var binding: FragmentSeePathsBinding
private lateinit var listener: OnAddLineRequestListener
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
...
}
private fun observeWidgetPath() {
pathViewModel.getUserWidgetPath().observe(viewLifecycleOwner, Observer {
if (it != null) {
lifecycleScope.launch {
val times = pathViewModel.fetchBusTime(it)
updateUI(it, times)
}
}
})
}
And here are the shotened ViewModel with the fetchBusTime method:
suspend fun fetchBusTime(path: Path): Pair<List<Time>?, List<Time>?> {
Log.v("fetchBusTimeUI", Thread.currentThread().name) // Main
// Some network requests made with Retrofit
val timesResponseStartPoint: GinkoTimesResponse? = repository.getTimes(
path.startingPoint.startName,
path.line.lineId,
path.isStartPointNaturalWay
)
val timesResponseEndPoint: GinkoTimesResponse? = repository.getTimes(
path.endingPoint.endName,
path.line.lineId,
path.isStartPointNaturalWay
)
return timesResponseStartPoint to timesResponseEndPoint
}
launch allows us to start a coroutine in the background and keep working in the meantime. Suspending functions may suspend the execution of the current coroutine without blocking the current thread. We can start a coroutine under any of the below dispatchers.
dipatcher.IO -> Network operation
dispatcher.Main -> On main thread
dispatcher.Default -> for CPU intensive operation
To explain you in detail, I am taking an example from the documentation :-
fun main() {
GlobalScope.launch { // launch new coroutine in background and continue
delay(1000L)
println("World!")
}
println("Hello,") // main thread continues here immediately
runBlocking { // but this expression blocks the main thread
delay(2000L) // ... while we delay for 2 seconds to keep JVM alive
}
}
The comments should speak for themselves. This will print “Hello,” immediately, and add “World!” after a second.
This is the same thing that will work with your code, the suspend function fetchBusTime() will be executed without blocking the thread and after the operation completed inside this method, it will execute updateUI(it, times).
For more details on this please go through this article here

Kotlin Coroutines the right way in Android

I'm trying to update a list inside the adapter using async, I can see there is too much boilerplate.
Is it the right way to use Kotlin Coroutines?
can this be optimized more?
fun loadListOfMediaInAsync() = async(CommonPool) {
try {
//Long running task
adapter.listOfMediaItems.addAll(resources.getAllTracks())
runOnUiThread {
adapter.notifyDataSetChanged()
progress.dismiss()
}
} catch (e: Exception) {
e.printStackTrace()
runOnUiThread {progress.dismiss()}
} catch (o: OutOfMemoryError) {
o.printStackTrace()
runOnUiThread {progress.dismiss()}
}
}
After struggling with this question for days, I think the most simple and clear async-await pattern for Android activities using Kotlin is:
override fun onCreate(savedInstanceState: Bundle?) {
//...
loadDataAsync(); //"Fire-and-forget"
}
fun loadDataAsync() = async(UI) {
try {
//Turn on busy indicator.
val job = async(CommonPool) {
//We're on a background thread here.
//Execute blocking calls, such as retrofit call.execute().body() + caching.
}
job.await();
//We're back on the main thread here.
//Update UI controls such as RecyclerView adapter data.
}
catch (e: Exception) {
}
finally {
//Turn off busy indicator.
}
}
The only Gradle dependencies for coroutines are: kotlin-stdlib-jre7, kotlinx-coroutines-android.
Note: Use job.await() instead of job.join() because await() rethrows exceptions, but join() does not. If you use join() you will need to check job.isCompletedExceptionally after the job completes.
To start concurrent retrofit calls, you can do this:
val jobA = async(CommonPool) { /* Blocking call A */ };
val jobB = async(CommonPool) { /* Blocking call B */ };
jobA.await();
jobB.await();
Or:
val jobs = arrayListOf<Deferred<Unit>>();
jobs += async(CommonPool) { /* Blocking call A */ };
jobs += async(CommonPool) { /* Blocking call B */ };
jobs.forEach { it.await(); };
How to launch a coroutine
In the kotlinx.coroutines library you can start new coroutine using either launch or async function.
Conceptually, async is just like launch. It starts a separate coroutine which is a light-weight thread that works concurrently with all the other coroutines.
The difference is that launch returns a Job and does not carry any resulting value, while async returns a Deferred - a light-weight non-blocking future that represents a promise to provide a result later. You can use .await() on a deferred value to get its eventual result, but Deferred is also a Job, so you can cancel it if needed.
Coroutine context
In Android we usually use two context:
uiContext to dispatch execution onto the Android main UI thread (for the parent coroutine).
bgContext to dispatch execution in background thread (for the child coroutines).
Example
//dispatches execution onto the Android main UI thread
private val uiContext: CoroutineContext = UI
//represents a common pool of shared threads as the coroutine dispatcher
private val bgContext: CoroutineContext = CommonPool
In following example we are going to use CommonPool for bgContext which limit the number of threads running in parallel to the value of Runtime.getRuntime.availableProcessors()-1. So if the coroutine task is scheduled, but all cores are occupied, it will be queued.
You may want to consider using newFixedThreadPoolContext or your own implementation of cached thread pool.
launch + async (execute task)
private fun loadData() = launch(uiContext) {
view.showLoading() // ui thread
val task = async(bgContext) { dataProvider.loadData("Task") }
val result = task.await() // non ui thread, suspend until finished
view.showData(result) // ui thread
}
launch + async + async (execute two tasks sequentially)
Note: task1 and task2 are executed sequentially.
private fun loadData() = launch(uiContext) {
view.showLoading() // ui thread
// non ui thread, suspend until task is finished
val result1 = async(bgContext) { dataProvider.loadData("Task 1") }.await()
// non ui thread, suspend until task is finished
val result2 = async(bgContext) { dataProvider.loadData("Task 2") }.await()
val result = "$result1 $result2" // ui thread
view.showData(result) // ui thread
}
launch + async + async (execute two tasks parallel)
Note: task1 and task2 are executed in parallel.
private fun loadData() = launch(uiContext) {
view.showLoading() // ui thread
val task1 = async(bgContext) { dataProvider.loadData("Task 1") }
val task2 = async(bgContext) { dataProvider.loadData("Task 2") }
val result = "${task1.await()} ${task2.await()}" // non ui thread, suspend until finished
view.showData(result) // ui thread
}
How to cancel a coroutine
The function loadData returns a Job object which may be cancelled. When the parent coroutine is cancelled, all its children are recursively cancelled, too.
If the stopPresenting function was called while dataProvider.loadData was still in progress, the function view.showData will never be called.
var job: Job? = null
fun startPresenting() {
job = loadData()
}
fun stopPresenting() {
job?.cancel()
}
private fun loadData() = launch(uiContext) {
view.showLoading() // ui thread
val task = async(bgContext) { dataProvider.loadData("Task") }
val result = task.await() // non ui thread, suspend until finished
view.showData(result) // ui thread
}
The complete answer is available in my article Android Coroutine Recipes
I think you can get rid of runOnUiThread { ... } by using UI context for Android applications instead of CommonPool.
The UI context is provided by the kotlinx-coroutines-android module.
We also have another option. if we use Anko library , then it looks like this
doAsync {
// Call all operation related to network or other ui blocking operations here.
uiThread {
// perform all ui related operation here
}
}
Add dependency for Anko in your app gradle like this.
implementation "org.jetbrains.anko:anko:0.10.5"
Like sdeff said, if you use the UI context, the code inside that coroutine will run on UI thread by default. And, if you need to run an instruction on another thread you can use run(CommonPool) {}
Furthermore, if you don't need to return nothing from the method, you can use the function launch(UI) instead of async(UI) (the former will return a Job and the latter a Deferred<Unit>).
An example could be:
fun loadListOfMediaInAsync() = launch(UI) {
try {
withContext(CommonPool) { //The coroutine is suspended until run() ends
adapter.listOfMediaItems.addAll(resources.getAllTracks())
}
adapter.notifyDataSetChanged()
} catch(e: Exception) {
e.printStackTrace()
} catch(o: OutOfMemoryError) {
o.printStackTrace()
} finally {
progress.dismiss()
}
}
If you need more help I recommend you to read the main guide of kotlinx.coroutines and, in addition, the guide of coroutines + UI
If you want to return some thing from background thread use async
launch(UI) {
val result = async(CommonPool) {
//do long running operation
}.await()
//do stuff on UI thread
view.setText(result)
}
If background thread is not returning anything
launch(UI) {
launch(CommonPool) {
//do long running operation
}.await()
//do stuff on UI thread
}
All the above answers are right, but I was having a hard time finding the right import for the UI from kotlinx.coroutines, it was conflicting with UI from Anko.
Its
import kotlinx.coroutines.experimental.android.UI
Here's the right way to use Kotlin Coroutines. Coroutine scope simply suspends the current coroutine until all child coroutines have finished their execution. This example explicitly shows us how child coroutine works within parent coroutine.
An example with explanations:
fun main() = blockingMethod { // coroutine scope
launch {
delay(2000L) // suspends the current coroutine for 2 seconds
println("Tasks from some blockingMethod")
}
coroutineScope { // creates a new coroutine scope
launch {
delay(3000L) // suspends this coroutine for 3 seconds
println("Task from nested launch")
}
delay(1000L)
println("Task from coroutine scope") // this line will be printed before nested launch
}
println("Coroutine scope is over") // but this line isn't printed until nested launch completes
}
Hope this helps.
Please find attached the implementation for a remote API call with Kotlin Coroutines & Retrofit library.
import android.view.View
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.test.nyt_most_viewed.NYTApp
import com.test.nyt_most_viewed.data.local.PreferenceHelper
import com.test.nyt_most_viewed.data.model.NytAPI
import com.test.nyt_most_viewed.data.model.response.reviews.ResultsItem
import kotlinx.coroutines.*
import javax.inject.Inject
class MoviesReviewViewModel #Inject constructor(
private val nytAPI: NytAPI,
private val nytApp: NYTApp,
appPreference: PreferenceHelper
) : ViewModel() {
val moviesReviewsResponse: MutableLiveData<List<ResultsItem>> = MutableLiveData()
val message: MutableLiveData<String> = MutableLiveData()
val loaderProgressVisibility: MutableLiveData<Int> = MutableLiveData()
val coroutineJobs = mutableListOf<Job>()
override fun onCleared() {
super.onCleared()
coroutineJobs.forEach {
it.cancel()
}
}
// You will call this method from your activity/Fragment
fun getMoviesReviewWithCoroutine() {
viewModelScope.launch(Dispatchers.Main + handler) {
// Update your UI
showLoadingUI()
val deferredResult = async(Dispatchers.IO) {
return#async nytAPI.getMoviesReviewWithCoroutine("full-time")
}
val moviesReviewsResponse = deferredResult.await()
this#MoviesReviewViewModel.moviesReviewsResponse.value = moviesReviewsResponse.results
// Update your UI
resetLoadingUI()
}
}
val handler = CoroutineExceptionHandler { _, exception ->
onMoviesReviewFailure(exception)
}
/*Handle failure case*/
private fun onMoviesReviewFailure(throwable: Throwable) {
resetLoadingUI()
Log.d("MOVIES-REVIEWS-ERROR", throwable.toString())
}
private fun showLoadingUI() {
setLoaderVisibility(View.VISIBLE)
setMessage(STATES.INITIALIZED)
}
private fun resetLoadingUI() {
setMessage(STATES.DONE)
setLoaderVisibility(View.GONE)
}
private fun setMessage(states: STATES) {
message.value = states.name
}
private fun setLoaderVisibility(visibility: Int) {
loaderProgressVisibility.value = visibility
}
enum class STATES {
INITIALIZED,
DONE
}
}

Categories

Resources