chaosmonk
chaosmonk

Reputation: 417

Running blocking CPU bound tasks on Kotlin coroutines

I have been experimenting with Kotlin and running blocking CPU tasks on kotlin coroutines. When things are blocking such as big cpu intensive computations we dont really have suspension but rather we need to launch things on different threads and let them run in parallel.

I managed to get the following code working as expected with async + Default dispatcher but wondered if it was gonna work with withContext and it did not.

fun cpuBlockingTasks() = runBlocking {
    val time = measureTimeMillis {
        val t1 = cpuTask(id = 1, blockTime = 500)
        val t2 = cpuTask(id = 2, blockTime = 2000)
        println("The answer is ${t1 + t2}")
    }
    println("Time taken: $time")
}
suspend fun cpuTask(id: Int, blockTime: Long): Int = withContext(Dispatchers.Default) {
    println("work $id start ${getThreadName()}")
    val res = doSomeCpuIntensiveTask(blockTime)
    println("work $id end ${getThreadName()}")
    res
}
fun doSomeCpuIntensiveTask(time: Long): Int {
    Thread.sleep(time) // to mimick actual thread blocking / cpu work
    return 1
}

This code completes in >2500 ms and runs on the same thread sequentially. I was expecting it to kick off the first coroutine in a thread, immediately return to the caller and kick of the second on a different thread but did not work like that. Anyone know why would that be and how it can be fixed without launching async coroutine in the caller function?

This it the output

work 1 start ForkJoinPool.commonPool-worker-5 @coroutine#1
work 1 end ForkJoinPool.commonPool-worker-5 @coroutine#1
work 2 start ForkJoinPool.commonPool-worker-5 @coroutine#1
work 2 end ForkJoinPool.commonPool-worker-5 @coroutine#1
The answer is 2
Time taken: 2523

Upvotes: 3

Views: 1287

Answers (1)

Andrei Tanana
Andrei Tanana

Reputation: 8442

You are not creating a new coroutine in cpuTask 1 and cpuTask 2. You are just switching context. It can be easily fixed with async:

fun cpuBlockingTasks() = runBlocking {
    val time = measureTimeMillis {
        val t1 = async { cpuTask(id = 1, blockTime = 500) }
        val t2 = async { cpuTask(id = 2, blockTime = 2000) }
        println("The answer is ${t1.await() + t2.await()}")
    }
    println("Time taken: $time") // Time taken: 2026
}

Upvotes: 2

Related Questions