Reputation: 1125
suspend funtions run on a seperate thread ? If not, then what is the performance benefit ?
suspend fun requestToken():Token {..} // takes 2 sec to complete
suspend fun createPost (token:Token){..} // takes 3 sec to complete
suspend fun postItem() {
val token = requestToken()
val post =createPost(token)
processPost(post)
}
So, when we reach at processPost(post) and if suspend function do not run on a seperate thread then we have to wait for requestToken() and createPost(token) method to complete (i.e 2+3= 5 seconds). As per the author, suspend is asyncronous,but if we are not spawning any new thread then how are we achieving asychronous behaviour ?
Upvotes: 4
Views: 2688
Reputation: 355
Background single thread or multiple background threads of a thread pool can be explicitly declared and then used, for example by passing it as a parameter, let's call this parameter "scheduler". The really cool thing about it is that initially started from the main thread it's automatically switched to the scheduler thread to execute a particular task on it and virtual machine kind of suspended or interrupted at this place and the thing which is even cooler the main thread gets unblocked and can execute something else while there is the task in background.
As soon as the task is finished, virtual machine sort of gets back to the point where it was suspended or interrupted before and then it resumes its execution from that point but now by also having the result returned from the background thread of the scheduler, below is the code snippet:
private val backgroundThread = ThreadPoolExecutor(1, 1, 15L, TimeUnit.SECONDS, LinkedBlockingQueue())
GlobalScope.launch(Dispatchers.Main, CoroutineStart.DEFAULT) {
postItem(backgroundThread))
}
suspend fun CoroutineScope.postItem(scheduler: ThreadPoolExecutor): Boolean {
val token = requestToken(scheduler)
val post = createPost(token, scheduler)
return processPost(post, scheduler)
}
private suspend fun CoroutineScope.requestToken(scheduler: ThreadPoolExecutor): String {
val def: Deferred<String?> = async(scheduler.asCoroutineDispatcher(), CoroutineStart.DEFAULT) {
val token = networkApi.requestToken()
}
return def.await() ?: ""
}
private suspend fun CoroutineScope.createPost(token: String, scheduler: ThreadPoolExecutor): String {
val def: Deferred<String?> = async(scheduler.asCoroutineDispatcher(), CoroutineStart.DEFAULT) {
val post = networkApi.createPost(token)
}
return def.await() ?: ""
}
private suspend fun CoroutineScope.processPost(post: String, scheduler: ThreadPoolExecutor): Boolean {
val def: Deferred<Boolean?> = async(scheduler.asCoroutineDispatcher(), CoroutineStart.DEFAULT) {
val result = networkApi.processPost(post)
}
return def.await() ?: false
}
Upvotes: -1
Reputation: 6124
Suspension-points can only be used within a coroutine context, for instance:
fun main() {
delay(1000)
}
Would not work because delay
is a suspending function and the compiler wouldn't know how to handle that without a coroutine. When you do have a coroutine it can use something called a dispatcher to control thread ownership. Suspending means that the thread is no longer used to execute that part of your program but its doing something else or going idle. The way it works is that you can have several coroutines working at the same time without having a thread for each, that thread can then execute parts of each coroutine up to a suspension point. Generally the idea is that you can view suspending functions as "generators" which have stages for producing a result.
suspend fun hello() {
println("Hello")
delay(1000) // suspend here, let someone else use the thread while we wait
println("World")
delay(1000) // suspend again, we can still use the thread while waiting
println("done")
}
Everytime this is suspended it uses the thread to work on another function until that suspends and the delay expires, at that point this function will eventually resume execution up to the next suspension point or finish execution entirely.
This is different to blocking code as it does not waste the thread by putting it into wait state but rather borrows it to another function. That way no other threads need to be created to have concurrency as you can still work on multiple functions without true parallelism, instead it uses concurrent execution of parts of the functions.
Coroutines don't necessarily protect you from blocking, if you call Thread.sleep(1000)
its still gonna be a blocking call. It is the responsibility of the programmer to use suspending equivalents to blocking functions in order to maximize effectiveness of this concept.
For more details, please read the documentation as its very detailed and helpful.
Upvotes: 1
Reputation: 200138
suspend is asynchronous
suspend fun
s execute synchronously with their caller. What you actually meant to say is "non-blocking" and that's a completely different story.
but if we are not spawning any new thread then how are we achieving asynchronous behaviour?
You are making the tacit assumption that all I/O must be blocking at some level. This is wrong. Non-blocking I/O works by pushing data to a send buffer and receiving notifications when there's data in the receive buffer. A suspend fun
hooks into this mechanism by suspending itself after pushing the data to a send buffer and installing a callback that will resume it when response data is ready in the receive buffer.
Upvotes: 5