Launching coroutines from a suspended function - kotlin

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 { ... }
}

Related

CoroutineScope launch work well inside direct call, but inside parameter scope launch, not working

fun test(coroutineScope: CoroutineScope){
coroutineScope.launch {
//FUNC 1
}
CoroutineScope(Dispatchers.XXX).launch {
//FUNC 2
}
}
FUNC 1 Not working but FUNC 2 is work well!
I don't know the difference between the two.
This is because coroutineScope is a function which returns whatever the lambda block returns. And since it is a function, you need to invoke it like coroutineScope(block = { ... }) or coroutineScope { ... }. While in your 2nd case, CoroutineScope(Dispatchers.XXX) returns a CoroutineScope and since launch is an extension function on CoroutineScope, it is valid.
The names are actually a bit confusing. coroutineScope is a function which takes a suspending lambda with CoroutineScope as the receiver while CoroutineScope(context) is a function which creates a CoroutineScope.
coroutineScope is generally used to capture the current coroutineContext inside a suspend function. For example,
suspend fun uploadTwoFiles() {
// Here we need to launch two coroutines in parallel,
// but since "launch" is an extension function on CoroutineScope,
// we need to get that CoroutineScope from somewhere.
coroutineScope {
// Here we have a CoroutineScope so we can call "launch"
launch { uploadFile1() }
launch { uploadFile2() }
}
// Also, note that coroutineScope finishes only when all the children coroutines have finished.
}
Using coroutineScope has this advantage that the coroutines launched inside will get cancelled when the parent coroutine cancels.

Kotlin coroutines not running in parallel?

I am trying to use Kotlin coroutines to run 2 queries in parallel. I get the result as expected, but they are running sequentially.
fun getSearchResults(
sql: SqlBuilder,
): Pair<Int?, List<T>> {
return runBlocking {
val one = async {
count(sql)
}
val two = async {
query(sql)
}
Pair(one.await(), two.await())
}
}
suspend fun count(sql: SqlBuilder): Int? {
return namedParameterJdbcTemplate.queryForObject(sql.countSql, sql.params) { rs: ResultSet, _: Int ->
rs.retrieveInteger("count")!!
}
}
suspend fun query(sql: SqlBuilder): List<T> {
return namedParameterJdbcTemplate.query(sql.sql, sql.params) { rs: ResultSet, _: Int ->
createEntity(rs)
}
}
Why is this not running in parallel? Also, why is my IDE telling me the suspend keywords are "redundant"? How do I fix this?
When you launch a coroutine in runBlocking, the default dispatcher is a single-threaded dispatcher running in the thread that called runBlocking. You should not be using runBlocking anyway, since it defeats the purpose of using coroutines. Your getSearchResults function is a blocking function.
The reason suspend is redundant on your other functions is that those functions are not calling any other suspend functions. Aside from being redundant, the suspend keyword implies that the function will cooperate with cancellation and by convention does not block. Your implementation is breaking both of those rules because they do not call other suspend functions and they are calling blocking functions without wrapping them in withContext with an appropriate dispatcher, so they are themselves blocking functions. Blocking suspend functions must never exist by convention.
So the solution is to change getSearchResults to be a suspend function, and use coroutineScope instead of runBlocking in it so it will run its children coroutines in parallel and it will not block. If it is a suspend function, it won't be redundant for your other functions that call it to be marked suspend.
suspend fun getSearchResults(
sql: SqlBuilder,
): Pair<Int?, List<T>> = coroutineScope {
val one = async {
count(sql)
}
val two = async {
query(sql)
}
Pair(one.await(), two.await())
}
If count and query are blocking IO functions, you should use async(Dispatchers.IO) to call them. But if they are suspend functions you don't need to specify a dispatcher.

Kotlin async function not run in parallel

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.

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.

Can I get return actual value if I use launch in Kotlin?

I'm learning Coroutines of Kotlin.
The Code A use async in the coroutines of Kotlin and I can use .await() on a deferred value to get its eventual result, so one.await() will return Int.
If I use launch in the coroutines, can I get the actual value just like one.await()?
Code A
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
suspend fun doSomethingUsefulOne(): Int {
delay(1000L) // pretend we are doing something useful here
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L) // pretend we are doing something useful here, too
return 29
}
There is no [standard non-hacky] way to get the result from launch. This is the whole point of having async.
The 2 functions launch and async have precisely one difference, which is that launch is fire-and-forget, and async allows to wait for a result.
Therefore, there should be no reason for you to want to use launch over async unless you don't need the result, so the question is quite suprising.
If you use launch, the "actual value" is Unit as you can see from the signature
fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job (source)
so you don't even need to start it.
If you pass a lambda to launch as in
launch { doSomethingUsefulOne() }
it is really
launch { doSomethingUsefulOne(); Unit }
and the value of doSomethingUsefulOne() is thrown away.
The short answer is NO
As you pointed out async and await will get you the result.
But Launch is used for a different purpose. Its purpose is to act as a bridge between Coroutine and NonCoroutine worlds.
Consider an example of a ViewModel whose coroutine world is controlled by viewModelScope
fun nonCoroutineWorldFunction() {
....
....
viewModelScope.launch {
// runs in coroutine world
}
....
....
viewModelScope.launch {
// runs in coroutine world
}
}
Launch can be considered something similar to FIRE AND FORGET. You just launch it to do its job rather than waiting for it to do its job