Kotlin async function not run in parallel - kotlin

I have a kotlin function just as following, And I expected it can wrap a sync IO action into async.
suspend fun <T> runIOAsync(f:suspend () -> T): Deferred<T> = coroutineScope{
async(Dispatchers.IO) {
f()
}
}
Then I have my code in the calling side like
runBlocking {
repeat(5) {
runIOAsync {
println(it)
println(Thread.currentThread())
Thread.sleep(3000)
println("After sleep $it")
}.await()
}
}
But the actual out put is
0
Thread[DefaultDispatcher-worker-1 #coroutine#2,5,main]
After sleep 0
1
Thread[DefaultDispatcher-worker-1 #coroutine#3,5,main]
After sleep 1
2
Thread[DefaultDispatcher-worker-1 #coroutine#4,5,main]
After sleep 2
3
Thread[DefaultDispatcher-worker-1 #coroutine#5,5,main]
After sleep 3
4
Thread[DefaultDispatcher-worker-1 #coroutine#6,5,main]
After sleep 4
Which seems all tasks from my function are executed serially. Any one can please help to give an explanation

Let's put aside runIOAsync for the moment.
You're using await() right after calling async(), which means that you're effectively waiting for the end of your async execution before executing the next one.
Instead, start all tasks first and then await all of them. For instance you can use awaitAll:
runBlocking {
List(5) {
async(Dispatchers.IO) {
println(it)
println(Thread.currentThread())
Thread.sleep(3000)
println("After sleep $it")
}
}.awaitAll()
}
Also, the way you're encapsulating the scope in runIOAsync is wrong, you will be waiting for the end of the async execution even without calling await() (coroutineScope is a suspending function that waits for all its child coroutines before resuming).
Instead, use coroutineScope to define the boundary of your coroutines executions, and you don't even have to await them. Since you don't need to get values from this code, you can also use launch instead of async here:
coroutineScope {
repeat(5) {
launch(Dispatchers.IO) {
println(it)
println(Thread.currentThread())
Thread.sleep(3000)
println("After sleep $it")
}
}
}
Declaring a suspend function returning a Deferred should be a red flag, and is quite confusing from an API standpoint: if you suspend it means you wait, if you return Deferred it means you don't wait (you immediately return a handle to some running computation). A function that does both would be quite weird.
If what you want is to make suspending code from IO-bound code, you can use instead the existing withContext function:
// this suspends until the code inside is done
withContext(Dispatchers.IO) {
// run some blocking IO code
}
However, note that this is independent from defining concurrent code. If you want to run multiple things concurrently, even with suspend functions, you'll need coroutine builders such as async or launch like in the above code.

Related

Differences between two coroutine launch in kotlin

What's the difference between CoroutineScope(dispatchers).launch{} and coroutineScope{ launch{}}?
Say I have the code below:
(you can go to Kotlin playground to run this snippet https://pl.kotl.in/U4eDY4uJt)
suspend fun perform(invokeAfterDelay: suspend () -> Unit) {
// not printing
CoroutineScope(Dispatchers.Default).launch {
delay(1000)
invokeAfterDelay()
}
// will print
coroutineScope {
launch {
delay(1000)
invokeAfterDelay()
}
}
}
fun printSomething() {
println("Counter")
}
fun main() {
runBlocking {
perform {
printSomething()
}
}
}
And as the comment stated, when using CoroutineScope().launch it won't invoke the print, however when using the other way, the code behaves as intended.
What's the difference?
Thanks.
Further question
New findings.
if I leave the perform function like this (without commenting out one of the coroutines)
suspend fun perform(invokeAfterDelay: suspend () -> Unit) {
CoroutineScope(Dispatchers.Default).launch {
delay(1000)
invokeAfterDelay()
}
coroutineScope {
launch {
delay(1000)
invokeAfterDelay()
}
}
}
then both of these coroutines will be executed 🤔Why?
CoroutineScope().launch {} and coroutineScope { launch{} } have almost nothing in common. The former just sets up an ad-hoc scope for launch to run against, completing immediately, and the latter is a suspendable function that ensures that all coroutines launched within it complete before it returns.
The snippet under your "Further question" is identical to the original one except for the deleted comments.
Whether or not the first coroutine prints anything is up to non-deterministic behavior: while perform is spending time within coroutineScope, awaiting for the completion of the inner launched coroutine, the first one may or may not complete itself. They have the same delay.

kotlin Coroutine multiple launch

How to make multiple launch like multiple thread in kotlin
I want to make that first second are working at the same time forever!!
Like this code...
runBlocking {
// first
launch{
first()
}
// second
launch{
second()
}
}
suspend fun first(){
// do something
delay(1000L)
// Recursive call
first()
}
suspend fun second(){
// do something
delay(1000L)
// Recursive call
second()
}
Your example code would work already, if it is the only running code in your application. If you need those two methods running in parallel to your application, wrap them in GlobalScope.launch:
GlobalScope.launch {
launch { first() }
launch { second() }
}
This will run forever until canceled and/or an exception inside is thrown. If you don't require too many resources inside a coroutine and release them properly when used, you should never have a problem with StackOverFlow.
In addition to recursive code: try to make a loop instead as it is suggested in the comments.
You can implement infinite execution with cycles
runBlocking {
launch { while(true) first() }
launch { while(true) second() }
}
suspend fun first(){
// do something
delay(1000L)
}
suspend fun second(){
// do something
delay(1000L)
}

Kotlin coroutines barrier: Wait for all coroutines to finish

I need to start many coroutines in a for loop and get a callback in the Main thread after all the tasks are completed.
What is the best way to do this?
//Main thread
fun foo(){
messageRepo.getMessages().forEach {message->
GlobalScope.launch {
doHardWork(message)
}
}
// here I need some callback to the Main thread that all work is done.
}
And there is no variant to iterate messages in CoroutineScope. The iteration must be done in the Main thread.
You can wait until all tasks will be completed with awaitAll and then execute your callback in the main thread with withContext
fun foo() {
viewModelScope.launch {
messageRepo.getMessages().map { message ->
viewModelScope.async(Dispatchers.IO) {
doHardWork(message)
}
}.awaitAll()
withContext(Dispatchers.Main) {
// here I need some callback that all work is done.
}
}
}

Launching coroutines from a suspended function

Given we have a suspending function but this is not a CoroutineScope, how can we launch other coroutines such that those are associated with the current scope of whatever runs this suspending function?
Every suspendable function has access to the global variable coroutineContext which you can trivially wrap in CoroutineScope, but that is not its intended purpose. It's there so you can check at any point whether your coroutine was cancelled, reach the debug info like the job name, etc.
In the words of Roman Elizarov in his recent Medium post:
suspend fun doNotDoThis() {
CoroutineScope(coroutineContext).launch {
println("I'm confused")
}
}
Do not do this!
A suspendable function should not fire off concurrent work that may go on after it returns. It should only use concurrency to achieve parallel decomposition of a task, and that means it will wait for all the child coroutines to complete.
You should decide to either use a plain function that is the receiver of CoroutineScope (signaling the intention to launch concurrent work) or use a suspendable function that awaits on the completion of all the work it initiated.
So, if you want parallel decomposition, then use a coroutineScope or, possibly a supervisorScope block:
coroutineScope {
launch {
// ... task to run in the background
}
// ... more work while the launched task runs in parallel
}
// All work done by the time we reach this line
coroutineScope is a suspendable function and it won't complete until all the coroutines it launched complete.
You can create an extension function on CoroutineScope or function with CoroutineScope as a parameter:
fun CoroutineScope.doThis() {
launch { ... }
}
fun doThatIn(scope: CoroutineScope) {
scope.launch { ... }
}
Also you can use coroutineScope or supervisorScope, depending on your needs:
suspend fun someFun() = coroutineScope {
launch { ... }
}
suspend fun someFun() = supervisorScope {
launch { ... }
}
You could just use withContext() or coroutineScope() for launching another coroutine:
withContext(coroutineContext) {
launch { ... }
}
While the second would override the Job of the context, but reuse the context:
coroutineScope {
launch { ... }
}

why "repeat" function is cancelable in kotlin

fun main(args: Array<String>) = runBlocking<Unit> {
val job = launch {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancel() // cancels the job
job.join() // waits for job's completion
println("main: Now I can quit.")
}
this code not check isActive or use suspend function but can canceled
Here's the analogy with Java threads:
1) Checking the interrupted flag explicitly:
while (!Thread.interrupted()) {
// loop code
}
2) Calling interruptible operations:
while (true) {
Thread.sleep(1);
// loop code
}
In both cases the thread will respond to a raised interrupt flag.
In coroutines, delay() is the counterpart of Thread.sleep() and the isActive flag is the counterpart of the Thread.interrupted flag.
Therefore, when you write
delay(1)
the coroutine will be scheduled off the thread and, when its time to resume comes, inside the continuation.resume() call it will first check the isActive flag. If it's raised, it will throw a CancellationException instead.
Because delay() is a suspend function.
Thread.sleep() is not a suspend function.
If you replace delay(500L) with Thread.sleep(500), then it will not be cancellable on time.