Is coroutineScope just like block function in Kotlin? [duplicate] - kotlin

I was reading Coroutine Basics trying to understand and learn it.
There is a part there with this code:
fun main() = runBlocking { // this: CoroutineScope
launch {
delay(200L)
println("Task from runBlocking")
}
coroutineScope { // Creates a new coroutine scope
launch {
delay(900L)
println("Task from nested launch")
}
delay(100L)
println("Task from coroutine scope") // This line will be printed before nested launch
}
println("Coroutine scope is over") // This line is not printed until nested launch completes
}
The output goes like so:
Task from coroutine scope
Task from runBlocking
Task from nested launch
Coroutine scope is over
My question is why this line:
println("Coroutine scope is over") // This line is not printed until nested launch completes
is called always last?
Shouldn't it be called since the:
coroutineScope { // Creates a new coroutine scope
....
}
is suspended?
There is also a note there:
The main difference between runBlocking and coroutineScope is that the latter does not block the current thread while waiting for all children to complete.
I dont understand how coroutineScope and runBlocking are different here? coroutineScope looks like its blocking since it only gets to the last line when it is done.
Can anyone enlighten me here?

I don't understand how coroutineScope and runBlocking are different here? coroutineScope looks like its blocking since it only gets to the last line when it is done.
There are two separate worlds: the suspendable world (within a coroutine) and the non-suspendable one. As soon as you enter the body of runBlocking, you are in the suspendable world, where suspend funs behave like blocking code and you can't get to the next line until the suspend fun returns. coroutineScope is a suspend fun that returns only when all the coroutines inside it are done. Therefore the last line must print at the end.
I copied the above explanation from a comment which seems to have clicked with readers. Here is the original answer:
From the perspective of the code in the block, your understanding is correct. The difference between runBlocking and coroutineScope happens at a lower level: what's happening to the thread while the coroutine is blocked?
runBlocking is not a suspend fun. The thread that called it remains inside it until the coroutine is complete.
coroutineScope is a suspend fun. If your coroutine suspends, the coroutineScope function gets suspended as well. This allows the top-level function, a non-suspending function that created the coroutine, to continue executing on the same thread. The thread has "escaped" the coroutineScope block and is ready to do some other work.
In your specific example: when your coroutineScope suspends, control returns to the implementation code inside runBlocking. This code is an event loop that drives all the coroutines you started within it. In your case, there will be some coroutines scheduled to run after a delay. When the time arrives, it will resume the appropriate coroutine, which will run for a short while, suspend, and then control will be again inside runBlocking.
While the above describes the conceptual similarities, it should also show you that runBlocking is a completely different tool from coroutineScope.
runBlocking is a low-level construct, to be used only in framework code or self-contained examples like yours. It turns an existing thread into an event loop and creates its coroutine with a Dispatcher that posts resuming coroutines to the event loop's queue.
coroutineScope is a user-facing construct, used to delineate the boundaries of a task that is being parallel-decomposed inside it. You use it to conveniently await on all the async work happening inside it, get the final result, and handle all failures at one central place.

The chosen answer is good but fails to address some other important aspects of the sample code that was provided. For instance, launch is non-blocking and is suppose to execute immediately. That is simply not true. The launch itself returns immediately BUT the code inside the launch does appear to be put into a queue and is only executed when any other launches that were previously put into the queue have completed.
Here's a similar piece of sample code with all the delays removed and an additional launch included. Without looking at the result below, see if you can predict the order in which the numbers are printed. Chances are that you will fail:
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
println("1")
}
coroutineScope {
launch {
println("2")
}
println("3")
}
coroutineScope {
launch {
println("4")
}
println("5")
}
launch {
println("6")
}
for (i in 7..100) {
println(i.toString())
}
println("101")
}
The result is:
3
1
2
5
4
7
8
9
10
...
99
100
101
6
The fact that number 6 is printed last, even after going through nearly 100 println have been executed, indicates that the code inside the last launch never gets executed until all non-blocking code after the launch has completed. But that is not really true either, because if that were the case, the first launch should not have executed until numbers 7 to 101 have completed. Bottom line? Mixing launch and coroutineScope is highly unpredictable and should be avoided if you expect a certain order in the way things should be executed.
To prove that code inside launches is placed into a queue and ONLY executed after ALL the non-blocking code has completed, run this (no coroutineScopes are used):
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
println("1")
}
launch {
println("2")
}
launch {
println("3")
}
for (i in 4..100) {
println(i.toString())
}
println("101")
}
This is the result you get:
4
5
6
...
101
1
2
3
Adding a CoroutineScope will break this behavior. It will cause all non-blocking code that follows the CoroutineScope to not be executed until ALL code prior to the CoroutineScope has completed.
It should also be noted that in this code sample, each of the launches in the queue are executed sequentially in the order that they are added to the queue and each launch will only execute AFTER the previous launch executes. This may make it appear that all launches share a common thread. This is not true. Each of them is given their own thread. However, if any code inside a launch calls a suspend function, the next launch in the queue is started immediately while the suspend function is being carried out. To be honest, this is very strange behavior. Why not just run all the launches in the queue asynchronously? While I don't know the internals of what goes on in this queue, my guess is that each launch in the queue does not get its own thread but all share a common thread. It is only when a suspend function is encountered does it appear that a new thread is created for the next launch in the queue. It may be done this way to save on resources.
To summarize, execution is done in this order:
Code inside a launch is placed inside a queue and are executed in the order that they are added.
Non-blocking code following a launch executes immediately before anything in the queue is executed.
A CoroutineScope blocks ALL code following it BUT will execute all the launch coroutines in the queue before resuming to the code following the CoroutineScope.

runBlocking is for you to block the main thread.
coroutineScope is for you to block the runBlocking.

Well, after having read all the answers here, I found none of them answered the question beyond repeating the wording of the fragments of the documentation.
So, I went on to search for an answer elsewhere and found it here. It practically shows the difference in behavior of coroutineScope and runBlocking (i.e. the difference between suspending and blocking)

runBlocking just blocks the current thread until inner coroutines will be completed. Here, thread that executes runBlocking will be blocked until the coroutine from coroutineScope will be finished.
First launch just won't allow the thread execute instructions that come after runBlocking, but will allow proceed to the instructions that come immediately after this launch block - that's why Task from coroutine scope is printed before than Task from runBlocking.
But nested coroutineScope in the context of runBlocking won't allow the thread to execute instructions that come after this coroutineScope block, because runBlocking will block the thread until the coroutine from coroutineScope will be finished completely. And that's why Coroutine scope is over will always come after Task from nested launch.

From this wonderful article https://jivimberg.io/blog/2018/05/04/parallel-map-in-kotlin/
suspend fun <A, B> Iterable<A>.pmap(f: suspend (A) -> B): List<B> = coroutineScope {
map { async { f(it) } }.awaitAll()
}
With runBlocking, we were not using Structured Concurrency, so an invocation of f could fail and all other executions would continue unfazed. And also we were not playing nice with the rest of the code. By using runBlocking we were forcefully blocking the thread until the whole execution of pmap finishes, instead of letting the caller decide how the execution should go.

Related

Cancelling a non-cancelable coroutine in Kotlin

Consider this non cancellable coroutine that works as its name implies.
fun main(args: Array<String>) = runBlocking {
val nonCancellableJob = launch(Dispatchers.Default) {
for (i in 1..1000) {
if (i % 100 == 0) {
println("Non cancellable iteration $i")
}
}
}
println("Cancelling non cancellable job...")
nonCancellableJob.cancelAndJoin()
}
Now, if I get rid of the explicit dispatcher Dispatchers.Default and use the inherited one i.e. launch {...} the coroutine gets cancelled immediately without printing anything. It seems that a non cancelling coroutine is being cancelled! Is it a bug or what?
When you don't use Dispatchers.Default then launch is inheriting dispatcher of runBlocking which seems to defer execution until needed.
Even though your coroutine cannot be canceled while running (since it doesn't have any activity check nor does it have suspension points) it's still possible to cancel it before it starts executing.
You can modify CoroutineStart of your launch builder to alter this behavior:
To execute it immediately when declared:
val nonCancellableJob = launch(start = CoroutineStart.UNDISPATCHED) { ... }
To prevent cancellation before it starts:
val nonCancellableJob = launch(start = CoroutineStart.ATOMIC) { ... }
The inherited dispatcher runs on the same thread where you launched it. This works such that runBlocking establishes a top-level loop that goes over the coroutines running inside it, resuming them one by one. It can't resume the next coroutine before the current one has suspended itself.
In your code, the top-level runBlocking coroutine created one child coroutine and then, without suspending, went on to do some more things: print a message and cancel the coroutine it just created. At this point the coroutine is still hanging in the limbo of having been created but not run. Your cancelAndJoin call cancels it in this state, before it has got its chance to hog the runBlocking dispatcher forever. It immediately changes the state to "completed through cancellation" and the whole program ends.

Kotlin coroutines - delay, how does it work?

I am quite used to using RX to handle concurrency, but, in my current job, we have a mix of AsyncTask, Executors + Handlers, Threads and some LiveData thrown in. Now we are thinking about moving towards using Kotlin Coroutines (and in fact have started using it in certain places in the codebase).
Therefore, I need to start wrapping my head around Coroutines, ideally drawing from my existing knowledge of concurrency tools to speed the process up.
I have tried following the Google codelab for them and whilst it's giving me a bit of understanding it's also raising lots of unanswered questions so I've tried getting my hands dirty by writing some code, debugging and looking at log outputs.
As I understand it, a coroutine is composed of 2 main building blocks; suspend functions which are where you do your work and coroutine contexts which is where you execute suspend functions such that you can have a handle on what dispatchers the coroutines will run on.
Here I have some code below, that behaves as I would expect. I have set up a coroutine context using Dispatchers.Main. So, as expected, when I launch the coroutine getResources it ends up blocking the UI thread for 5 seconds due to the Thread.sleep(5000):
private const val TAG = "Coroutines"
class MainActivity : AppCompatActivity(), CoroutineScope {
override val coroutineContext: CoroutineContext = Job() + Dispatchers.Main
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
log("onCreate", "launching coroutine")
launch {
val resource = getResource()
log("onCreate", "resource fetched: $resource")
findViewById<TextView>(R.id.textView).text = resource.toString()
}
log("onCreate", "coroutine launched")
}
private suspend fun getResource() : Int {
log("getResource", "about to sleep for 5000ms")
Thread.sleep(5000)
log("getResource", "finished fetching resource")
return 1
}
private fun log(methodName: String, toLog: String) {
Log.d(TAG,"$methodName: $toLog: ${Thread.currentThread().name}")
}
}
When I run this code, I see the following logs:
2020-05-28 11:42:44.364 9819-9819/? D/Coroutines: onCreate: launching coroutine: main
2020-05-28 11:42:44.376 9819-9819/? D/Coroutines: onCreate: coroutine launched: main
2020-05-28 11:42:44.469 9819-9819/? D/Coroutines: getResource: about to sleep for 5000ms: main
2020-05-28 11:42:49.471 9819-9819/com.example.coroutines D/Coroutines: getResource: finished fetching resource: main
2020-05-28 11:42:49.472 9819-9819/com.example.coroutines D/Coroutines: onCreate: resource fetched: 1: main
As you can see, all the logs originated from the main thread, and there is a 5 second gap between the log before and after the Thread.sleep(5000). During that 5 second gap, the UI thread is blocked, I can confirm this by just looking at the emulator; it doens't render any UI because onCreate is blocked.
Now, if I update the getResources function to use the suspend fun delay(5000) instead of using Thread.sleep(5000) like so:
private suspend fun getResource() : Int {
log("getResource", "about to sleep for 5000ms")
delay(5000)
log("getResource", "finished fetching resource")
return 1
}
Then what I end up seeing confuses me. I understand delay isn't the same as Thread.sleep, but because I am running it within the coroutine context which is backed by Dispatchers.Main, I expected to see the same result as using Thread.sleep.
Instead, what I see is the UI thread is not blocked while the 5 second delay is happening, and the logs look like:
2020-05-28 11:54:19.099 10038-10038/com.example.coroutines D/Coroutines: onCreate: launching coroutine: main
2020-05-28 11:54:19.111 10038-10038/com.example.coroutines D/Coroutines: onCreate: coroutine launched: main
2020-05-28 11:54:19.152 10038-10038/com.example.coroutines D/Coroutines: getResource: about to sleep for 5000ms: main
2020-05-28 11:54:24.167 10038-10038/com.example.coroutines D/Coroutines: getResource: finished fetching resource: main
2020-05-28 11:54:24.168 10038-10038/com.example.coroutines D/Coroutines: onCreate: resource fetched: 1: main
I can see the UI thread is not blocked in this case as the UI renders whilst the delay is taking place and then the text view is updated after 5 seconds.
So, my question is, how does delay, in this case, not block the UI thread (even though the logs in my suspend function still indicate that the function is running on the main thread...)
Think of suspend functions as a way to use a function that takes a callback, but doesn't require you to to pass that callback into it. Instead, the callback code is everything under the suspend function call.
This code:
lifecycleScope.launch {
myTextView.text = "Starting"
delay(1000L)
myTextView.text = "Processing"
delay(2000L)
myTextView.text = "Done"
}
Is somewhat like:
myTextView.text = "Starting"
handler.postDelayed(1000L) {
myTextView.text = "Processing"
handler.postDelayed(2000L) {
myTextView.text = "Done"
}
}
Suspend functions should never be expected to block. If they do, they have been composed incorrectly. Any blocking code in a suspend function should be wrapped in something that backgrounds it, like withContext or suspendCancellableCoroutine (which is lower level because it works directly with the coroutine continuation).
If you try to write a suspend function like this:
suspend fun myDelay(length: Long) {
Thread.sleep(length)
}
you will get a compiler warning for "Inappropriate blocking method call". If you push it to a background dispatcher, you won't get the warning:
suspend fun myDelay(length: Long) = withContext(Dispatchers.IO) {
Thread.sleep(length)
}
If you try to send it to Dispatchers.Main, you will get the warning again, because the compiler considers any blocking code on the Main thread to be incorrect.
This should give you and idea of how a suspend function should operate, but keep in mind the compiler cannot always recognize a method call as blocking.
The best way to connect your existing intuition with the world of coroutines is to make this mental mapping: whereas in the classical world, the OS schedules threads to CPU cores (preemptively suspending them as needed), a dispatcher schedules coroutines to threads. Coroutines can't be preemptively suspended, this is where the cooperative nature of coroutine concurrency comes in.
With that in mind:
because I am running it within the coroutine context which is backed by Dispatchers.Main, I expected to see the same result as using Thread.sleep.
delay(delayTime) simply suspends the coroutine and schedules its resumption delayTime later. Therefore you should expect to see a very different result than with Thread.sleep, which never suspends a coroutine and keeps occupying its thread, a situation comparable to one where Thread.sleep() wouldn't allow the CPU core to run other stuff, but would busy-wait.

how coroutine scope builder flow works

Kotlin says
runBlocking method blocks the current thread for waiting
coroutineScope just suspends, releasing the underlying thread for other usages.
hence runBlocking is a regular function and coroutineScope is a suspending function
fun main() = runBlocking { // this: CoroutineScope
launch {
delay(200L)
println("Task from runBlocking")
}
coroutineScope { // Creates a coroutine scope
launch {
delay(500L)
println("Task from nested launch")
}
delay(100L)
println("Task from coroutine scope") // This line will be printed before the nested launch
}
println("Coroutine scope is over") // This line is not printed until the nested launch completes
}
in above example what i expect is :-
runBlocking blocks main thread and launch will executed and it comes to delay(200L)
So, underlying coroutine is released and runs coroutineScope and comes to delay(500L) & delay(100L)
So, again underlying coroutine is released and it should print println("Coroutine scope is over").
This is what my understanding on runBlocking and coroutineScope. Which is not working as expected.
the Output is
Task from coroutine scope
Task from runBlocking
Task from nested launch
Coroutine scope is over
Can anyone kindly explain in a easy way to understand this.
launch causes the block to be executed asynchronously, so the call to launch returns immediately, and the coroutine continues its running and doesn't wait for the execution of the launched block.
Therefore, immediately after runBlocking is called, the first and the second launch are called one after another, and immediately after that the coroutine is suspended on delay(100L).
After 100ms the coroutine is resumed and prints "Task from coroutine scope", and then the execution of the nested coroutine-scope's block ends. A coroutine-scope always waits for the end of execution of all the jobs it has launched, so it waits here for 500ms.
Meanwhile, the two launched blocks are executed, so "Task from runBlocking" is printed first (after 200ms from the start), and then "Task from nested launch" is printed (after 500ms from the start).
Eventually, after the internal launched job has been completed, the internal coroutine-scope finishes waiting, and the external coroutine continues and prints "Coroutine scope is over".
This is the story. I hope it helps a little to understand how the code is executed and why the order of printing is like that.
I modified the code a little
fun main() = runBlocking(Dispatchers.Default) {
var i = 1
launch {
println("Task from runBlocking")
while (i < 10) {
delay(30L)
println(i++)
}
}
coroutineScope { // Creates a coroutine scope
launch {
delay(200L)
println("Task from nested launch")
}
delay(100L)
println("Task from coroutine scope") // This line will be printed before the nested launch
}
println("Coroutine scope is over")
}
Output
Task from runBlocking
1
2
3
Task from coroutine scope
4
5
6
Task from nested launch
Coroutine scope is over
7
8
9
the observation i made is,
delay(100L) is equal to approximate 3 times delay(30L)
delay(200L) is equal to approximate 6 times delay(30L)
So, after 3 Task from coroutine scope and after 6 Task from nested launch is printed.
then exactly after this Coroutine scope is over but you can still see the loop printed 7,8,9.
This is because like runBlocking coroutineScope waits for all its members to execute by suspending underlying threads. But understand, those threads first work on members of coroutineScope not on runBlocking.
Hence, it is printing Task from coroutine scope and Task from nested launch before Coroutine scope is over

Coroutines: runBlocking vs coroutineScope

I was reading Coroutine Basics trying to understand and learn it.
There is a part there with this code:
fun main() = runBlocking { // this: CoroutineScope
launch {
delay(200L)
println("Task from runBlocking")
}
coroutineScope { // Creates a new coroutine scope
launch {
delay(900L)
println("Task from nested launch")
}
delay(100L)
println("Task from coroutine scope") // This line will be printed before nested launch
}
println("Coroutine scope is over") // This line is not printed until nested launch completes
}
The output goes like so:
Task from coroutine scope
Task from runBlocking
Task from nested launch
Coroutine scope is over
My question is why this line:
println("Coroutine scope is over") // This line is not printed until nested launch completes
is called always last?
Shouldn't it be called since the:
coroutineScope { // Creates a new coroutine scope
....
}
is suspended?
There is also a note there:
The main difference between runBlocking and coroutineScope is that the latter does not block the current thread while waiting for all children to complete.
I dont understand how coroutineScope and runBlocking are different here? coroutineScope looks like its blocking since it only gets to the last line when it is done.
Can anyone enlighten me here?
I don't understand how coroutineScope and runBlocking are different here? coroutineScope looks like its blocking since it only gets to the last line when it is done.
There are two separate worlds: the suspendable world (within a coroutine) and the non-suspendable one. As soon as you enter the body of runBlocking, you are in the suspendable world, where suspend funs behave like blocking code and you can't get to the next line until the suspend fun returns. coroutineScope is a suspend fun that returns only when all the coroutines inside it are done. Therefore the last line must print at the end.
I copied the above explanation from a comment which seems to have clicked with readers. Here is the original answer:
From the perspective of the code in the block, your understanding is correct. The difference between runBlocking and coroutineScope happens at a lower level: what's happening to the thread while the coroutine is blocked?
runBlocking is not a suspend fun. The thread that called it remains inside it until the coroutine is complete.
coroutineScope is a suspend fun. If your coroutine suspends, the coroutineScope function gets suspended as well. This allows the top-level function, a non-suspending function that created the coroutine, to continue executing on the same thread. The thread has "escaped" the coroutineScope block and is ready to do some other work.
In your specific example: when your coroutineScope suspends, control returns to the implementation code inside runBlocking. This code is an event loop that drives all the coroutines you started within it. In your case, there will be some coroutines scheduled to run after a delay. When the time arrives, it will resume the appropriate coroutine, which will run for a short while, suspend, and then control will be again inside runBlocking.
While the above describes the conceptual similarities, it should also show you that runBlocking is a completely different tool from coroutineScope.
runBlocking is a low-level construct, to be used only in framework code or self-contained examples like yours. It turns an existing thread into an event loop and creates its coroutine with a Dispatcher that posts resuming coroutines to the event loop's queue.
coroutineScope is a user-facing construct, used to delineate the boundaries of a task that is being parallel-decomposed inside it. You use it to conveniently await on all the async work happening inside it, get the final result, and handle all failures at one central place.
The chosen answer is good but fails to address some other important aspects of the sample code that was provided. For instance, launch is non-blocking and is suppose to execute immediately. That is simply not true. The launch itself returns immediately BUT the code inside the launch does appear to be put into a queue and is only executed when any other launches that were previously put into the queue have completed.
Here's a similar piece of sample code with all the delays removed and an additional launch included. Without looking at the result below, see if you can predict the order in which the numbers are printed. Chances are that you will fail:
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
println("1")
}
coroutineScope {
launch {
println("2")
}
println("3")
}
coroutineScope {
launch {
println("4")
}
println("5")
}
launch {
println("6")
}
for (i in 7..100) {
println(i.toString())
}
println("101")
}
The result is:
3
1
2
5
4
7
8
9
10
...
99
100
101
6
The fact that number 6 is printed last, even after going through nearly 100 println have been executed, indicates that the code inside the last launch never gets executed until all non-blocking code after the launch has completed. But that is not really true either, because if that were the case, the first launch should not have executed until numbers 7 to 101 have completed. Bottom line? Mixing launch and coroutineScope is highly unpredictable and should be avoided if you expect a certain order in the way things should be executed.
To prove that code inside launches is placed into a queue and ONLY executed after ALL the non-blocking code has completed, run this (no coroutineScopes are used):
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
println("1")
}
launch {
println("2")
}
launch {
println("3")
}
for (i in 4..100) {
println(i.toString())
}
println("101")
}
This is the result you get:
4
5
6
...
101
1
2
3
Adding a CoroutineScope will break this behavior. It will cause all non-blocking code that follows the CoroutineScope to not be executed until ALL code prior to the CoroutineScope has completed.
It should also be noted that in this code sample, each of the launches in the queue are executed sequentially in the order that they are added to the queue and each launch will only execute AFTER the previous launch executes. This may make it appear that all launches share a common thread. This is not true. Each of them is given their own thread. However, if any code inside a launch calls a suspend function, the next launch in the queue is started immediately while the suspend function is being carried out. To be honest, this is very strange behavior. Why not just run all the launches in the queue asynchronously? While I don't know the internals of what goes on in this queue, my guess is that each launch in the queue does not get its own thread but all share a common thread. It is only when a suspend function is encountered does it appear that a new thread is created for the next launch in the queue. It may be done this way to save on resources.
To summarize, execution is done in this order:
Code inside a launch is placed inside a queue and are executed in the order that they are added.
Non-blocking code following a launch executes immediately before anything in the queue is executed.
A CoroutineScope blocks ALL code following it BUT will execute all the launch coroutines in the queue before resuming to the code following the CoroutineScope.
runBlocking is for you to block the main thread.
coroutineScope is for you to block the runBlocking.
Well, after having read all the answers here, I found none of them answered the question beyond repeating the wording of the fragments of the documentation.
So, I went on to search for an answer elsewhere and found it here. It practically shows the difference in behavior of coroutineScope and runBlocking (i.e. the difference between suspending and blocking)
runBlocking just blocks the current thread until inner coroutines will be completed. Here, thread that executes runBlocking will be blocked until the coroutine from coroutineScope will be finished.
First launch just won't allow the thread execute instructions that come after runBlocking, but will allow proceed to the instructions that come immediately after this launch block - that's why Task from coroutine scope is printed before than Task from runBlocking.
But nested coroutineScope in the context of runBlocking won't allow the thread to execute instructions that come after this coroutineScope block, because runBlocking will block the thread until the coroutine from coroutineScope will be finished completely. And that's why Coroutine scope is over will always come after Task from nested launch.
From this wonderful article https://jivimberg.io/blog/2018/05/04/parallel-map-in-kotlin/
suspend fun <A, B> Iterable<A>.pmap(f: suspend (A) -> B): List<B> = coroutineScope {
map { async { f(it) } }.awaitAll()
}
With runBlocking, we were not using Structured Concurrency, so an invocation of f could fail and all other executions would continue unfazed. And also we were not playing nice with the rest of the code. By using runBlocking we were forcefully blocking the thread until the whole execution of pmap finishes, instead of letting the caller decide how the execution should go.

Does Kotlin suspend funtion runs on a separate thread?

suspend funtions run on a seperate thread ?
If not, then what is the performance benefit ?
suspend fun requestToken():Token {..} // takes 2 sec to complete
suspend fun createPost (token:Token){..} // takes 3 sec to complete
suspend fun postItem() {
val token = requestToken()
val post =createPost(token)
processPost(post)
}
So, when we reach at processPost(post) and if suspend function do not run on a seperate thread then we have to wait for requestToken() and createPost(token) method
to complete (i.e 2+3= 5 seconds). As per the author, suspend is asyncronous,but if we are not spawning any new thread then how are we achieving asychronous behaviour ?
suspend is asynchronous
suspend funs execute synchronously with their caller. What you actually meant to say is "non-blocking" and that's a completely different story.
but if we are not spawning any new thread then how are we achieving asynchronous behaviour?
You are making the tacit assumption that all I/O must be blocking at some level. This is wrong. Non-blocking I/O works by pushing data to a send buffer and receiving notifications when there's data in the receive buffer. A suspend fun hooks into this mechanism by suspending itself after pushing the data to a send buffer and installing a callback that will resume it when response data is ready in the receive buffer.
Suspension-points can only be used within a coroutine context, for instance:
fun main() {
delay(1000)
}
Would not work because delay is a suspending function and the compiler wouldn't know how to handle that without a coroutine. When you do have a coroutine it can use something called a dispatcher to control thread ownership. Suspending means that the thread is no longer used to execute that part of your program but its doing something else or going idle. The way it works is that you can have several coroutines working at the same time without having a thread for each, that thread can then execute parts of each coroutine up to a suspension point. Generally the idea is that you can view suspending functions as "generators" which have stages for producing a result.
suspend fun hello() {
println("Hello")
delay(1000) // suspend here, let someone else use the thread while we wait
println("World")
delay(1000) // suspend again, we can still use the thread while waiting
println("done")
}
Everytime this is suspended it uses the thread to work on another function until that suspends and the delay expires, at that point this function will eventually resume execution up to the next suspension point or finish execution entirely.
This is different to blocking code as it does not waste the thread by putting it into wait state but rather borrows it to another function. That way no other threads need to be created to have concurrency as you can still work on multiple functions without true parallelism, instead it uses concurrent execution of parts of the functions.
Coroutines don't necessarily protect you from blocking, if you call Thread.sleep(1000) its still gonna be a blocking call. It is the responsibility of the programmer to use suspending equivalents to blocking functions in order to maximize effectiveness of this concept.
For more details, please read the documentation as its very detailed and helpful.
Background single thread or multiple background threads of a thread pool can be explicitly declared and then used, for example by passing it as a parameter, let's call this parameter "scheduler". The really cool thing about it is that initially started from the main thread it's automatically switched to the scheduler thread to execute a particular task on it and virtual machine kind of suspended or interrupted at this place and the thing which is even cooler the main thread gets unblocked and can execute something else while there is the task in background.
As soon as the task is finished, virtual machine sort of gets back to the point where it was suspended or interrupted before and then it resumes its execution from that point but now by also having the result returned from the background thread of the scheduler, below is the code snippet:
private val backgroundThread = ThreadPoolExecutor(1, 1, 15L, TimeUnit.SECONDS, LinkedBlockingQueue())
GlobalScope.launch(Dispatchers.Main, CoroutineStart.DEFAULT) {
postItem(backgroundThread))
}
suspend fun CoroutineScope.postItem(scheduler: ThreadPoolExecutor): Boolean {
val token = requestToken(scheduler)
val post = createPost(token, scheduler)
return processPost(post, scheduler)
}
private suspend fun CoroutineScope.requestToken(scheduler: ThreadPoolExecutor): String {
val def: Deferred<String?> = async(scheduler.asCoroutineDispatcher(), CoroutineStart.DEFAULT) {
val token = networkApi.requestToken()
}
return def.await() ?: ""
}
private suspend fun CoroutineScope.createPost(token: String, scheduler: ThreadPoolExecutor): String {
val def: Deferred<String?> = async(scheduler.asCoroutineDispatcher(), CoroutineStart.DEFAULT) {
val post = networkApi.createPost(token)
}
return def.await() ?: ""
}
private suspend fun CoroutineScope.processPost(post: String, scheduler: ThreadPoolExecutor): Boolean {
val def: Deferred<Boolean?> = async(scheduler.asCoroutineDispatcher(), CoroutineStart.DEFAULT) {
val result = networkApi.processPost(post)
}
return def.await() ?: false
}