What is the order of execution with coroutines? - android

Consider the following code in kotlin.
val scope = CoroutineScope(Dispatchers.Main + Job())
scope.launch {
println("inside coroutine")
}
println("outside coroutine")
We create a coroutine in the Main(UI) thread and there is some code after the coroutine.
I know it doesn´t make much sense to do that in real code, but it´s just a theoretical question.
Considering that the coroutine runs in the Main thread, why println("outside coroutine") is ALWAYS executed first?
I would have expected that sometimes i would see first outside coroutine and other times, first inside coroutine, kind of like two threads.
Who (OS or Coroutines implementation) decides that the coe outside the coroutine is run first?

Considering that the coroutine runs in the Main thread, why println("outside coroutine") is ALWAYS executed first?
Let's imagine that your code instead was this:
someView.post {
println("inside post")
}
println("outside post")
Here, we create a Runnable (lambda expression) and pass that to post() on some View. post() says that the Runnable will be run() on the main application thread... eventually. That Runnable is put on the work queue that the Looper powering the main application thread uses, and it gets executed when that Runnable gets to the top of the queue (more or less — the details are messier IIRC but not important here).
But if you are executing this code on the main application thread, println("outside post") will always be printed first. The Runnable is placed onto the queue to be executed later, but you are still executing on the main application thread, and so even if the queue were empty, that Runnable will not run until you return control of the main application thread back to Android. So, after the call to post(), execution continues with println("outside post").
Under the covers, Dispatchers.Main is basically using post() (again, the details are more complicated but not too important for this discussion). So, when you launch() the coroutine, that lambda expression gets queued up to be executed eventually on the main application. But, you are already on the main application thread, so execution continues normally, and the println("outside post") gets printed before the coroutine gets a chance to do anything.
Suppose that your code instead was:
val scope = CoroutineScope(Dispatchers.Main + Job())
scope.launch {
println("inside coroutine")
}
scope.launch {
println("inside another coroutine")
}
Now you are in a situation where in theory either of those lines could be printed first. You are queuing up both lambda expressions, and it is up to the dispatcher to decide what to run on what thread at what point. In practice, it would not surprise me if "inside coroutine" is always printed first, as a simple implementation of Dispatchers.Main would use FIFO ordering in the absence of other constraints (e.g., a coroutine is blocked on I/O). However, you should not assume a particular order of invocation of those two coroutines.

Related

Accessing View on Dispatchers.IO on coroutine does not crash the app, why ? But UI can only be accessed via MAIN Thread in Android

Here is the code I am using :
(application as TestApp).applicationScope.launch(Dispatchers.IO) {
println("Thread 2 "+Thread.currentThread().name)
binding.username.setText("text2")
}
(application as TestApp).applicationScope.launch(Dispatchers.Default) {
println("Thread 3 "+Thread.currentThread().name)
binding.username.setText("text3")
}
Here I am accessing the TextView on Dispatchers.IO and Dispatchers.Default thread from an Activity, which is not a main thread. Still the app runs properly and does not throw any exception.
The printed thread names are :
Thread 3 DefaultDispatcher-worker-4
Thread 2 DefaultDispatcher-worker-2
Why is that ?
It throws for me (using lifecycleScope in a Fragment):
CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
What's your output for those println statements, are the thread names different? What thread are you inflating binding on?
It's possible that under the hood, in your particular environment, those coroutines aren't being executed on different threads. Like when I run it, both the IO and Default dispatchers end up printing a thread name beginning with Defaultdispatcher-worker- which implies they're starting with the Default dispatcher's thread pool. And there's such a thing as an unconfined dispatcher:
The Dispatchers.Unconfined coroutine dispatcher starts a coroutine in the caller thread, but only until the first suspension point. After suspension it resumes the coroutine in the thread that is fully determined by the suspending function that was invoked.
Since your coroutines don't suspend (e.g. with delay) it would be possible that they'd hit the UI updates before execution is moved to another thread. Like it says there, Dispatchers.Unconfined is an edge-case dispatcher not recommended for use in general, but it's possible that something like that is happening behind the scenes for you, handling the dispatch more efficiently. (Not for me though!) You could try adding a delay to each before the UI touching, and see if it changes anything
(I don't know much at all about the underlying management of coroutines, I could be completely off base. I'm just pointing this out as a possible line of investigation since you're seeing weird behaviour!)

What happens using a Dispatcher v/s Main Thread in Android

In the context of Android, What is the difference between a dispatcher vs main thread.
As per my understanding referring the documentation,
It is backed by a shared pool of threads on JVM. By default, the
maximal level of parallelism used by this dispatcher is equal to the
number of CPU cores, but is at least two. Level of parallelism X
guarantees that no more than X tasks can be executed in this
dispatcher in parallel.
Will it spawn a new thread or there will be as per the log name DefaultDispatcher-worker-1 a worker that will communicate with the pool of threads other than the main to handle a block of co-routine or worker itself is a Co-routine?
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val timeInMillis = measureTimeMillis {
GlobalScope.launch(Dispatchers.Default) {
Log.d(TAG, "Starting coroutine in thread ${Thread.currentThread().name}")
val answer = doNetworkCall()
withContext(Dispatchers.Main) {
Log.d(TAG, "Setting text in thread ${Thread.currentThread().name}")
}
}
}
Log.d(TAG, "(The operation took $timeInMillis ms)")
}
A Dispatcher is essentially a thread pool. When you use launch(Dispatchers.Default), the Thread used to run the code in the coroutine will be obtained from Dispatchers.Default. Each time there is a suspend function call in the coroutine, when the coroutine resumes after that call, it might be resuming on a different Thread instance coming from the same Dispatchers.Default thread pool.
"DefaultDispatcher-worker-1" is the name of a literal Thread instance that came from the Dispatcher.Default's pool.
withContext is itself a suspend function call, so any code after the withContext block will also be resumed on some thread from Dispatchers.Default. (There is none in your example).
The code inside withContext(Dispatchers.Main) will be run on a thread from Dispatchers.Main.
Dispatchers.Main is a special Dispatcher that has only one thread, and that thread is the same Main Thread used by the OS.
You should rarely ever need to use GlobalScope and it is discouraged because it doesn't allow easy management of coroutine lifecycles. If you use lifecycleScope instead, your coroutines will be automatically cancelled when the associated Activity or Fragment is shut down. This is usually what you want, because an Activity or Fragment shouldn't be continuing to do work after it's gone.
Usually on Android, most coroutines should be launched from lifecycleScope or viewModelScope and should not need to have a Dispatcher specified since these scopes by default use Dispatchers.Main which is usually what you want. (Actually they use a different dispatcher called Dispatchers.Main.immediate which also uses the main thread but also can run the first part of a coroutine immediately without deferring to the next frame of the main thread loop. Not a distinction you need to worry about.) You can wrap the pieces of your coroutine that need other dispatchers in withContext. You don't need to do this if you are only calling suspend functions. By convention it is up to suspend functions to internally delegate to a specific Dispatcher if they need to.
An exception to the above paragraph might be if you're launching a coroutine in viewModelScope that does some blocking work and never touches anything that is main-thread-only. Then you can skip withContext and specify a dispatcher explicitly with launch.

Do suspend functions execute in another thread and come back with the result?

I've looked at plenty of articles online but I'm still a bit confused as to what happens specifically, step by step, when suspend functions are suspended in coroutines.
I know that a suspend function, under the hood, is just a regular function with a Continuation parameter that allows it to resume, but my confusion is regarding where that suspend function or coroutine goes and where it comes back once resumed.
I've heard a few people saying "they don't necessarily come back to the same thread" and I don't get it, can someone please explain this to me step by step?
TLDR;
There is no guarantee, it may or may not,
it really depends that on the following points:
Is the dispatcher is multi-threaded?
Is there any dispatcher override in between?
LONG Answer
A coroutine has a CoroutineContext that specify how it behaves, where it run.
A CoroutineContext is mainly build up with four elements: Job, CoroutineName, CoroutineExceptionHandler and Dispatcher.
Its responsibility of the dispatcher to dispatch the coroutine. A dispatcher can be paused to stop coroutines to even run (this is useful in unit testing) mentioned here in the android conference talk, it may be a single-threaded dispatcher just like Dispatchers.Main, it has an event-loop like javascript has.
So, it really depends that on the following points:
Is the dispatcher is multi-threaded?
For example: This will run on single thread.
suspend fun main() {
val dispatcherScope = CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher())
val job = dispatcherScope.launch {
repeat(10) {
launch {
println("I'm working in thread ${Thread.currentThread().name}")
// every coroutine on same thread
}
}
}
job.join()
}
Run it here, other single threaded dispatchers: Dispatchers.Main
Is there any dispatcher override in between?
With the same context, if we override the dispatcher before launch, it will change the thread even if original context is based on single-threaded event-loop, each coroutine will run on different thread creating 10 different thread:
dispatcherScope.launch {
repeat(10) {
launch(Dispatchers.IO) {
println("I'm working in thread ${Thread.currentThread().name}")
// every coroutine on same thread
}
}
}
Run it here, other multi-threaded Dispatchers: Dispatchers.Default, Executor based dispatcher, Dispatchers.Unconfined (this launch coroutine in any free thread).
The short answer is: they execute somewhere and come back with the result to somewhere.
The long(er) explanation for the short answer:
"Somewhere" might be the same thread, it might be a different thread - it depends on the dispatcher and in many cases, the current state of the dispatcher. For instance, the contents of a SequenceScope will (by default) run on the same thread.
Another case where a suspend function might run on the same thread is if it's using the same dispatcher as the calling function.
Dispatchers can also share threads between them to save on thread creation (and just keep a count of the maximum number of parallel operations for their own tasks), so even switching between different dispatchers that each use thread pools may not result in using a different thread.
As far as people saying "they don't necessarily come back to the same thread," this is correct, for similar reasons. Keep in mind that your dispatcher may have many threads, and the one that you were using before you got suspended might be occupied with a different function right now, so the dispatcher will simply pick a different thread to run your code on.
When the coroutine suspends, the underlying Java method returns a special COROUTINE_SUSPENDED value. If the calling function is also suspendable, it also returns the object, and so the execution returns to the innermost plain, non-suspendable function. This function typically runs an event loop, where event handlers are calls to continuation.resume(). So now it is ready to take the next handler from the queue and resume another coroutine.
When you call continuation.resume(), the continuation itself knows about the dispatcher in charge of the coroutine and delegates to it. If the current thread isn't owned by that dispatcher, it dispatches an event to another event loop, the one served by the dispatcher's thread pool. This way the dispatcher controls the thread where the corutine resumes.

Difference between Kotlin Coroutine ,Android Async Task and Async await

I went through the Kotlin Coroutine, I understood how it works but I have a confusion between Kotlin coroutine & Android Async.execute() & Async await. The Kotlin coroutine runs in the background and does not block on the UI thread but the same thing happens when we start android AsyncTask(with the methods doInBackground onPostExecute and onProgressUpdate overridden), it also does the computation in a background thread and publishes the result on the UI thread.
Async-await returns a Deffered object means the result will obviously going to be returned in future.
Can Anyone Explain what's the difference between these.
Let's try to break this down:
The Kotlin coroutine runs in the background
A coroutine CAN run in the background
does not block on the UI thread
Let's talk about what a coroutine is:
A coroutine can be thought of as the code that gets passed to one of the coroutine builder functions i.e. launch {}
By definition when a coroutine is launched, suspend functions within it do not block the corresponding Thread when they are reached; they "pause" the coroutine.
When the suspension point is reached, it is as if you were telling the code to "call you back later" when the result is available; a suspension point can be though of as a callback.
Let's look at an example:
fun main() {
val job = MainScope().launch {
doSomeWork()
}
suspend fun doSomeWork() {/*expensive work goes here*/}
}
When doSomeWork() is reached the code will suspend the coroutine, i.e. the suspend modifier is indicating to the coroutine framework that it can go do some other coroutine related work and then come back to this point when doSomeWork() is done.
Since this coroutine is launched using MainScope() it will be launched in the main Thread. That is why I said that coroutines CAN run in a background Thread but not always do so. In this case it does not, but it still does not block the UI Thread.
In the other hand, AsyncTask was (it is deprecated as of API 30) a mechanism that performed a tasks in a background Thread and posted the result back to the UI Thread
For the difference between CoroutineScope.async{} and CoroutineScope.launch{} we can look at the return values for each. As I showed in the example. launch{} returns a Job which is a representation of the lifecycle of the coroutine itself. Using the Job you can cancel() or join() the coroutine; you have control over its lifecycle. As you mention, async{} returns a Deffered<T> which is a representation of a future value. When await() is called on the Deffered<T> the coroutine is suspended until the result is ready to be consumed.

Is await blocking the UI thread on android?

I have been wrapping my head around coroutines and I was wondering about the following code.
I have the following operation on my onCreate().
asyncJob = GlobalScope.launch(Dispatchers.Main) {
val name = async(Dispatchers.Default) { queryDevices() }.await()
mDeviceName.text = deviceName
}
Printing this out the order of execution seems to be before "name" is
on UI thread and after name is set, it is on the UI thread as well.
The queryDevicesMethod() is in a background thread as expected.
But I wanted to know what await() is actually doing when calling it on the UI thread?
Is it blocking theUI thread until await returns?
Coroutines will not block the thread on suspending. The Kotlin compiler generates a state machine that detaches and attaches the coroutines from the thread, see https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md.
In your case GlobalScope.launch(Dispatchers.Main) starts a new coroutine confined to the UI thread. Then async() starts a new coroutine confined to another dispatcher. The invocation of await() is a suspending function and will detach the first coroutine from the UI thread, waiting for the completion of the async-coroutine.
BTW:
You should not use async and await in one statement. That makes no sense.
What you really want is to run the queryDevices()-function from another dispatcher, but not asynchronously from the perspective of coroutines. In this case you should use withContext()

Categories

Resources