Does async block when using await - kotlin

In the following code two asyncs are run:
import kotlinx.coroutines.*
import kotlin.system.*
fun main() = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
two.await()
one.await()
println("Finished")
}
}
suspend fun doSomethingUsefulOne(): Int {
delay(3000L)
println("first")
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L)
println("second")
return 29
}
Why is "Finished" not printed first? The docs says this about await:
Awaits for completion of this value without blocking a thread

The complete quote is:
Awaits for completion of this value without blocking a thread and
resumes when deferred computation is complete, returning the resulting
value or throwing the corresponding exception if the deferred was
cancelled.
Source
without blocking a thread:
The execution inside other async coroutine is not blocked. Track the time and you'll see that there was no 'pause' in time execution.
resumes when deferred computation is complete: the main thread will wait to continue on following code (println("Finished")), since you force it by calling await() to wait for the returned value. But the executions inside coroutines are not blocked.
EDIT
Considering the comments, I agree with AndroidDev on misleading documentation. It should be changed to:
Awaits for completion of this value without blocking other coroutines or
threads...
or
Awaits for completion of this value blocking only the current thread ...

By slightly modifying the code, one will gain some insights. Let's add the elapsed time and the current thread to the print statements to see, what's going on:
Execute on kotlin playground
import kotlinx.coroutines.*
import kotlin.system.*
private val started = System.currentTimeMillis()
private fun debug(s: String) {
val elapsed = System.currentTimeMillis() - started
println("[$elapsed] $s -- ${Thread.currentThread()}")
}
fun main() = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
debug("awaiting")
two.await()
one.await()
debug("Finished")
}
debug("time = $time")
}
suspend fun doSomethingUsefulOne(): Int {
delay(3000L)
debug("first")
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L)
debug("second")
return 29
}
When run with -Dkotlinx.coroutines.debug one should see something similar to
[46] awaiting -- Thread[main #coroutine#1,5,main]
[1056] second -- Thread[main #coroutine#3,5,main]
[3054] first -- Thread[main #coroutine#2,5,main]
[3054] Finished -- Thread[main #coroutine#1,5,main]
[3054] time = 3013 -- Thread[main #coroutine#1,5,main]
There is only one single thread.
If await had blocked the thread, the program could not have terminated.

Related

Why can I cancel a Flow without either invoking yield or determining isActive() identification in Kotlin?

I have read the article.
There are two approaches to making computation code cancellable. The first one is to periodically invoke a suspending function that checks for cancellation. There is a yield function that is a good choice for that purpose. The other one is to explicitly check the cancellation status.
I know Flow is suspending functions.
I run Code B , and get Result B as I expected.
I think I can't making computation Code A cancellable, but in fact I can click "Stop" button to cancel Flow after I click "Start" button to emit Flow, why?
Code A
class HandleMeter: ViewModel() {
var currentInfo by mutableStateOf(2.0)
private var myJob: Job?=null
private fun soundDbFlow() = flow {
while (true) {
val data = (0..1000).random().toDouble()
emit(data)
}
}
fun calCurrentAsynNew() {
myJob?.cancel()
myJob = viewModelScope.launch(Dispatchers.IO) {
soundDbFlow().collect {currentInfo=it }
}
}
fun cancelJob(){
myJob?.cancel()
}
}
#Composable
fun Greeting(handleMeter: HandleMeter) {
var currentInfo = handleMeter.currentInfo
Column(
modifier = Modifier.fillMaxSize(),
) {
Text(text = "Current ${currentInfo}")
Button(
onClick = { handleMeter.calCurrentAsynNew() }
) {
Text("Start")
}
Button(
onClick = { handleMeter.cancelJob() }
) {
Text("Stop")
}
}
}
Code B
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch(Dispatchers.IO) {
cal()
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin()
println("main: Now I can quit.")
}
suspend fun cal() {
val startTime = System.currentTimeMillis()
var nextPrintTime = startTime
var i = 0
while (i < 5) {
if ( System.currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
Result B
job: I'm sleeping 0 ...
job: I'm sleeping 1 ...
job: I'm sleeping 2 ...
main: I'm tired of waiting!
job: I'm sleeping 3 ...
job: I'm sleeping 4 ...
main: Now I can quit.
Add Content:
To Tenfour04: Thanks!
If the following content you said is true. I think Code C can be canceled when system finish the operation doBigBlockingCalculation() at one time, right? Why do I need Code D?
Since emit() is a suspend function, your Flow is able to interrupt and end the coroutine the next time the emit() function is called in that while loop.
Code C
private fun complicatedFlow() = flow {
while (true) {
val data = (0..1_000_000).doBigBlockingCalculation()
emit(data)
}
}.flowOn(Dispatchers.Default) // since the calculation is blocking
Code D
private fun complicatedFlow() = flow {
while (true) {
val data = (0..1_000_000)
.chunked(100_000)
.flatMap {
it.doBigBlockingCalculation().also { yield() }
}
emit(data)
}
}.flowOn(Dispatchers.Default) // since the calculation is blocking
A Flow on its own is cold. Its a wrapper around some suspend functions that will run when collect() or some other terminal suspending function is called on the Flow.
In your Code A, when the Job is cancelled, it is cancelling the coroutine that called collect on the Flow. collect is a suspend function, so that cancellation will propagate down to the function you defined inside soundDbFlow(). Since emit() is a suspend function, your Flow is able to interrupt and end the coroutine the next time the emit() function is called in that while loop.
Here's an example for how you could use this knowledge:
Suppose your function had to do a very long calculation like this:
private fun complicatedFlow() = flow {
while (true) {
val data = (0..1_000_000).doBigBlockingCalculation()
emit(data)
}
}.flowOn(Dispatchers.Default) // since the calculation is blocking
Now if you tried to cancel this flow, it would work, but since the data line is a very slow operation that is not suspending, the Flow will still complete this very long calculation for no reason, eating up resources for longer than necessary.
To resolve this problem, you could break your calculation up into smaller pieces with yield() calls in between. Then the Flow can be cancelled more promptly.
private fun complicatedFlow() = flow {
while (true) {
val data = (0..1_000_000)
.chunked(100_000)
.flatMap {
it.doBigBlockingCalculation().also { yield() }
}
emit(data)
}
}.flowOn(Dispatchers.Default) // since the calculation is blocking
Not a perfect example. It's kind of wasteful to chunk a big IntRange. An IntRange takes barely any memory, but chunked turns it into Lists containing every value in the range.
It has to do with CoroutineScopes and children of coroutines.
When a parent coroutine is canceled, all its children are canceled as well.
More here:
https://kotlinlang.org/docs/coroutine-context-and-dispatchers.html#children-of-a-coroutine

Making Kotlin Suspend Function Cancellable

I want to make a suspend function cancellable, but isActive isn't accessible. Is this just handled automatically?
suspend fun coolFunction() {
while (isActive) {
/* Do cool stuff */
}
}
To cooperate with cancellation, you can periodically suspend, most simply done by calling yield()
suspend fun coolFunction() {
while (true) {
yield()
/* Do cool stuff */
}
}
You can also support cancellation by checking CoroutineScope.isActive. But a suspend function on its own doesn't have direct access to the CoroutineScope it was called from. You would have to use something like coroutineContext[Job]!!.isActive, which is clumsy. isActive is more useful when you're directly composing a coroutine with something like launch rather than a suspend function that could be called from any scope.
You can cancel the job or coroutineScope that suspending function is running in so the suspending function will cancel.
private suspend fun CoroutineScope.cancelComputation() {
println("cancelComputation()")
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
// WARNING 🔥 isActive is an extension property that is available inside
// the code of coroutine via CoroutineScope object.
while (isActive) { // cancellable computation loop
// print a message twice a second
if (System.currentTimeMillis() >= nextPrintTime) {
println("I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L) // delay a bit
println("main: I'm tired of waiting!")
job.cancelAndJoin() // cancels the job and waits for its completion println("main: Now I can quit.")
/*
Prints:
cancelComputation()
I'm sleeping 0 ...
I'm sleeping 1 ...
I'm sleeping 2 ...
main: I'm tired of waiting!
*/
}

What return will await() in Kotlin?

I'm learning Coroutines of Kotlin. The Code A will get the Result A.
I know "async is used to start a coroutine that computes some result. The result is represented by an instance of Deferred.".
So val one will return Deferred, but what return will one.await()? It seem that one.await() will return Int, right?
Code A
import kotlinx.coroutines.*
import kotlin.system.*
fun main() = runBlocking {
val time = measureTimeMillis {
val one = async { doJob1() }
val two = async { doJob2() }
val a1=one.await()
val a2=two.await()
println("[testSequential] Result ${a1+a2}")
}
println("[testSequential] Completed in $time ms")
}
suspend fun doJob1(): Int {
println("Job1 doing")
delay(2000L)
println("Job1 done")
return 10
}
suspend fun doJob2(): Int {
println("Job2 doing")
delay(1000L)
println("Job2 done")
return 20
}
Result A
Job1 doing
Job2 doing
Job2 done
Job1 done
[testSequential] Result 30
[testSequential] Completed in 2016 ms
According to kotlin Document
async starts a separate coroutine which is a light-weight thread that works concurrently with all the other coroutines.
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.
let's say
val one = async { Fun() }
//one is type of Deferred
val mystring = one.await()
//mystring is type of string
fun Fun(): String{
return "hello"
}
You just need to look at the signature:
// in Deferred<T>
abstract suspend fun await(): T
So when called on a Deferred<Int> it will indeed return Int; when called on, say, a Deferred<YourVeryUsefulClass> it will return YourVeryUsefulClass.

How to execute a blocking coroutine call in kotlin and specify the thread

Is it possible to execute a blocking coroutine call (one that returns a value) while also providing the thread to execute the call on (I don't want to use the main thread, which is the default)?
I suppose you are looking for async(context):
import kotlinx.coroutines.*
fun someCalc(): Int {
println(Thread.currentThread().name) // Prints different thread
Thread.sleep(500L)
return 42
}
fun main() {
val result = runBlocking {
val deferred = async(Dispatchers.Default) {
someCalc()
}
deferred.await()
}
println(result)
}
You can also use newSingleThreadContext() to create a context confined to a single thread and use it instead of Dispatchers.Default
EDIT: As #Rene said, there is a better solution:
val result = runBlocking {
withContext(Dispatchers.Default) {
someCalc()
}
}
If you just have a thread that's already running, and you have no control over the code it runs, then there is nothing you can do about it. The thread must be running a top-level event loop so you can inject the code to run from the outside.
If, by any chance, you have this kind of control, or you can decide which Runnable the thread will run to begin with, here's some pretty hacky code that manages to set up an event loop and submit a coroutine to it:
val myContextFuture = CompletableFuture<CoroutineContext>()
thread(name = "my-thread") {
runBlocking {
myContextFuture.complete(coroutineContext)
coroutineContext[Job]!!.join()
}
}
val myContext = myContextFuture.get()
Here's how you would run a coroutine on myContext:
val result = withContext(myContext) {
println("Running on thread ${currentThread().name}")
3
}
println("Result of the coroutine: $result")
I wouldn't recommend using this kind of code in production.

Async doesn't appear to run concurrently

The following code uses 2 async function calls:
import kotlinx.coroutines.*
import kotlin.system.*
fun main() = runBlocking<Unit> {
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(3000L) // pretend we are doing something useful here
println("first")
return 13
}
suspend fun doSomethingUsefulTwo(): Int {
delay(1000L) // pretend we are doing something useful here, too
println("second")
return 29
}
The println results in "first" being printed first followed by "second". But according to the docs, these asyncs should be running concurrently. But since the first one has a delay of 3 seconds, why is its println running first? In fact, it doesn't even matter if I put the println before or after the delay. I get the same result.
The reason you have a gap between the 2 functions is the way you have called them in your print line:
println("The answer is ${one.await() + two.await()}")
Try changing .await() to .launch().
Await() calls the function and then stops until it is complete.