Async doesn't appear to run concurrently - kotlin

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.

Related

Does IntelliJ provide someway to check if two methods are really called in parallel when used kotlinx.coroutines.async

I am trying to call at same time (parallelism) two diferent endpoints. For reach that I decide to use coroutineScope and async. Here is the basic implementation:
suspend private fun myMethod(): List<Any> {
return runBlocking {
listOf(
async { method1() },
async { method2() }
).map { it.await() }
}
}
Method1 and method2 are simple request to diferent endpoints. Method1 can be called in the same time as method2 because they are completly independent.
How can I check in IntelliJ if they are really been called at same time?
The closest answer I found was https://stackoverflow.com/a/63219225/4148175 and I read https://blog.jetbrains.com/kotlin/2020/07/kotlin-1-4-rc-debugging-coroutines/ but I couldn't find someway to check if method1 and method2 are realy executed in parallel.
You can track the execution time of each one and compare the results. The total time should be approximately equal to the longest execution time of all the functions if they ran in parallel, for example.
suspend fun firstFun() {
val time = measureTimeMillis {
delay(1000)
}
println("firstFun completed in $time ms.")
}
suspend fun secondFun() {
val time = measureTimeMillis {
delay(1500)
}
println("secondFun completed in $time ms.")
}
fun thirdFun() = runBlocking {
val time = measureTimeMillis {
listOf(
async { firstFun() },
async { secondFun() }
).awaitAll()
}
println("thirdFun completed in $time ms.")
}
Executing thirdFun() gives something like this:
firstFun completed in 1001 ms.
secondFun completed in 1500 ms.
thirdFun completed in 1577 ms.
The total time is 1577 ms which is approximately max(fistFun, secondFun), meaning that those two functions actually ran in parallel.

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.

Does async block when using await

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.

Testing coroutines in Kotlin

I have this simple test about a crawler that is supposed to call the repo 40 times:
#Test
fun testX() {
// ...
runBlocking {
crawlYelp.concurrentCrawl()
// Thread.sleep(5000) // works if I un-comment
}
verify(restaurantsRepository, times(40)).saveAll(restaurants)
// ...
}
and this implementation:
suspend fun concurrentCrawl() {
cities.map { loc ->
1.rangeTo(10).map { start ->
GlobalScope.async {
val rests = scrapYelp.scrap(loc, start * 10)
restaurantsRepository.saveAll(rests)
}
}
}
}
But... I get this:
Wanted 40 times:
-> at ....testConcurrentCrawl(CrawlYelpTest.kt:46)
But was 30 times:
(the 30 is changing all the time; so it seems the test is not waiting...)
Why does it pass when I do the sleep? It should not be needed given I run blocking..
BTW, I have a controller that is supposed to be kept asynchronous:
#PostMapping("crawl")
suspend fun crawl(): String {
crawlYelp.concurrentCrawl()
return "crawling" // this is supposed to be returned right away
}
Thanks
runBlocking waits for all suspend functions to finish, but as concurrentCrawl basically just starts new jobs in new threads with GlobalScope.async currentCrawl, and therefore runBlocking, is done after all jobs were started and not after all of this jobs have finished.
You have to wait for all jobs started with GlobalScope.async to finish like this:
suspend fun concurrentCrawl() {
cities.map { loc ->
1.rangeTo(10).map { start ->
GlobalScope.async {
val rests = scrapYelp.scrap(loc, start * 10)
restaurantsRepository.saveAll(rests)
}
}.awaitAll()
}
}
If you want to wait for concurrentCrawl() to finish outside of concurrentCrawl() then you have to pass the Deferred results to the calling function like in the following example. In that case the suspend keyword can be removed from concurrentCrawl().
fun concurrentCrawl(): List<Deferred<Unit>> {
return cities.map { loc ->
1.rangeTo(10).map { start ->
GlobalScope.async {
println("hallo world $start")
}
}
}.flatten()
}
runBlocking {
concurrentCrawl().awaitAll()
}
As mentioned in the comments: In this case the async method does not return any value so it is better to use launch instead:
fun concurrentCrawl(): List<Job> {
return cities.map { loc ->
1.rangeTo(10).map { start ->
GlobalScope.launch {
println("hallo world $start")
}
}
}.flatten()
}
runBlocking {
concurrentCrawl().joinAll()
}
You could also just use MockK for this (and so much more).
MockK's verify has a timeout : Long parameter specifically for handling these races in tests.
You could leave your production code as it is, and change your test to this:
import io.mockk.verify
#Test
fun `test X`() = runBlocking {
// ...
crawlYelp.concurrentCrawl()
verify(exactly = 40, timeout = 5000L) {
restaurantsRepository.saveAll(restaurants)
}
// ...
}
If the verify is successful at any point before 5 seconds, it'll pass and move on. Else, the verify (and test) will fail.

Kotlin coroutines progress counter

I'm making thousands of HTTP requests using async/await and would like to have a progress indicator. I've added one in a naive way, but noticed that the counter value never reaches the total when all requests are done. So I've created a simple test and, sure enough, it doesn't work as expected:
fun main(args: Array<String>) {
var i = 0
val range = (1..100000)
range.map {
launch {
++i
}
}
println("$i ${range.count()}")
}
The output is something like this, where the first number always changes:
98800 100000
I'm probably missing some important detail about concurrency/synchronization in JVM/Kotlin, but don't know where to start. Any tips?
UPDATE: I ended up using channels as Marko suggested:
/**
* Asynchronously fetches stats for all symbols and sends a total number of requests
* to the `counter` channel each time a request completes. For example:
*
* val counterActor = actor<Int>(UI) {
* var counter = 0
* for (total in channel) {
* progressLabel.text = "${++counter} / $total"
* }
* }
*/
suspend fun getAssetStatsWithProgress(counter: SendChannel<Int>): Map<String, AssetStats> {
val symbolMap = getSymbols()?.let { it.map { it.symbol to it }.toMap() } ?: emptyMap()
val total = symbolMap.size
return symbolMap.map { async { getAssetStats(it.key) } }
.mapNotNull { it.await().also { counter.send(total) } }
.map { it.symbol to it }
.toMap()
}
The explanation what exactly makes your wrong approach fail is secondary: the primary thing is fixing the approach.
Instead of async-await or launch, for this communication pattern you should instead have an actor to which all the HTTP jobs send their status. This will automatically handle all your concurrency issues.
Here's some sample code, taken from the link you provided in the comment and adapted to your use case. Instead of some third party asking it for the counter value and updating the GUI with it, the actor runs in the UI context and updates the GUI itself:
import kotlinx.coroutines.experimental.*
import kotlinx.coroutines.experimental.channels.*
import kotlin.system.*
import kotlin.coroutines.experimental.*
object IncCounter
fun counterActor() = actor<IncCounter>(UI) {
var counter = 0
for (msg in channel) {
updateView(++counter)
}
}
fun main(args: Array<String>) = runBlocking {
val counter = counterActor()
massiveRun(CommonPool) {
counter.send(IncCounter)
}
counter.close()
println("View state: $viewState")
}
// Everything below is mock code that supports the example
// code above:
val UI = newSingleThreadContext("UI")
fun updateView(newVal: Int) {
viewState = newVal
}
var viewState = 0
suspend fun massiveRun(context: CoroutineContext, action: suspend () -> Unit) {
val numCoroutines = 1000
val repeatActionCount = 1000
val time = measureTimeMillis {
val jobs = List(numCoroutines) {
launch(context) {
repeat(repeatActionCount) { action() }
}
}
jobs.forEach { it.join() }
}
println("Completed ${numCoroutines * repeatActionCount} actions in $time ms")
}
Running it prints
Completed 1000000 actions in 2189 ms
View state: 1000000
You're losing writes because i++ is not an atomic operation - the value has to be read, incremented, and then written back - and you have multiple threads reading and writing i at the same time. (If you don't provide launch with a context, it uses a threadpool by default.)
You're losing 1 from your count every time two threads read the same value as they will then both write that value plus one.
Synchronizing in some way, for example by using an AtomicInteger solves this:
fun main(args: Array<String>) {
val i = AtomicInteger(0)
val range = (1..100000)
range.map {
launch {
i.incrementAndGet()
}
}
println("$i ${range.count()}") // 100000 100000
}
There's also no guarantee that these background threads will be done with their work by the time you print the result and your program ends - you can test it easily by adding just a very small delay inside launch, a couple milliseconds. With that, it's a good idea to wrap this all in a runBlocking call which will keep the main thread alive and then wait for the coroutines to all finish:
fun main(args: Array<String>) = runBlocking {
val i = AtomicInteger(0)
val range = (1..100000)
val jobs: List<Job> = range.map {
launch {
i.incrementAndGet()
}
}
jobs.forEach { it.join() }
println("$i ${range.count()}") // 100000 100000
}
Have you read Coroutines basics? There's exact same problem as yours:
val c = AtomicInteger()
for (i in 1..1_000_000)
launch {
c.addAndGet(i)
}
println(c.get())
This example completes in less than a second for me, but it prints some arbitrary number, because some coroutines don't finish before main() prints the result.
Because launch is not blocking, there's no guarantee all of coroutines will finish before println. You need to use async, store the Deferred objects and await for them to finish.