Jetbrains in every article about kotlin speaks about asynchronous programming. But I can't understand why are they called asynchronous? As I understand kotlin coroutines - this is a state machine with pre-init thread pools. We have a worker thread pool and an io thread pool. For me this is just a multi threading programming. If we send a blocking code to the coroutine, a thread will be blocked. If we use async method(from default coroutines library) , it gives us an illusion asynchronous work, but this nothing but send 'job' to another thread.
Another question if we use async io with coroutines. But this is IO API async, not kotlin coroutines. Java has not good io async api in comparison with other languages (can be wrong). .NET as I know already rebuilt theirs async api (as IOCP) to use C# task and .NET has dedicated thread pool for waiting all io of apps, so one thread can handle many IO operations. But kotlin coroutines are not integrated to java nio and when we call nio from the coroutine (with or without Dispatcher.IO) we just ask a thread wait a data from nio. Java NIO has its own thread pool for epoll or iocp, so with kotlin coroutines we are creating overhead when asking Dispatcher.IO to give us a thread for waiting result from NIO, and after that NIO implementation creates own thread (pool) for waiting data from socket. Instead of one thread (pool) for waiting, we have two now.
So coroutines just allow us send job to another thread in an easy way. You can't do multiple things with one thread at the same time if your api doesn't implemented in async way with kotlin coroutines.
CodePudding user response:
They talk about async programming because coroutines primarily (but not limited to) are sold as a library to make asynchronous programming easier(subjective). but as you rightly point out there is nothing async about coroutines themselves. if you executed blocking code in a coroutine it will block the underlying thread.
but the point to understand is that coroutines are only really advantageous when coupled with suspending functions, where a thread does nothing but wait for the result(callback). so instead of waiting you can use the same thread to do ten more such calls. another major advantage is that async code written using coroutines is much easier to write and maintain. for example, following is async calling using callbacks
fun callAPI(){
getToken{ token ->
auth(token){ authResult ->
doSomething(authResult){ finalResult ->
// use final result
}
}
}
}
this can be reduced to following using coroutines and suspending functions
fun callAPI() = scope.launch(){
val token = getToken()
val authResult = auth(token)
val finalResult = doSomething(authResult)
}
Now its possible for you to use coroutines for launching multiple long running blocking tasks but you wouldn't see any advantage. because in that case coroutines are nothing but a useless abstraction above threads.
CodePudding user response:
I would try to look at the problem from a different angle. Coroutines are based on suspending functions. Suspending functions are asynchronous.
The fact that a coroutine is a state machine is irrelevant there. A state machine could be translated to a chain of asynchronous functions and vice versa.
Instead of looking at Dispatchers as "thread pools", you should look at them as a Reactor pattern or an Event Loop. Yes, you can block an event loop. However, it doesn't mean that the event loop isn't asynchronous or that you should do it.
What you call "an illusion of asynchronous job" is "an illusion" only if you don't use suspending functions inside it. Since a significant part of Kotlin code is suspending (flow, Ktor, etc.), in reality, most of your code will be effectively asynchronous.
Java's NIO has different abstractions from Kotlin: callbacks. Those can be translated to a coroutine using suspendCancellableCoroutine
The last part is mostly wrong:
coroutines just allow us send job to another thread in an easy way. You can't do multiple things with one thread at the same time if your api doesn't implemented in async way with kotlin coroutines.
A single Dispatcher thread will context switch between multiple coroutines, unless one of the coroutines is blocking: doesn't use any suspend functions and performs IO or an CPU intensive task.