https://kotlinlang.org logo
Title
d

Dmytro Serdiuk

04/01/2021, 4:10 PM
Hello! I’m new to Kotlin and checking the documentation, I found some statement for this code:
fun main() = runBlocking<Unit> {
    val counter = counterActor() // create the actor withContext(Dispatchers.Default) {
        massiveRun { 
            counter.send(IncCounter)
        } 
}
    // send a message to get a counter value from an actor
    val response = CompletableDeferred<Int>() 
    counter.send(GetCounter(response)) 
    println("Counter = ${response.await()}")     
    counter.close() // shutdown the actor
}
Actor is more efficient than locking under load, because in this case it always has work to do and it does not have to switch to a different context at all. How to understand it?And how is it efficient than locks?
Really need some info about it, as stuck and couldn’t figure out. Thanks
g

gildor

04/03/2021, 6:24 AM
It's comparing to solutions, when you have multiple threads which use lock to run some operation using lock, solution with actor just doesn't require lock and thread context switch
d

Dmytro Serdiuk

04/04/2021, 1:44 PM
But lock in the terms of coroutines is just suspending action for the critical section and it will the same suspending as send method to the channel, if the channel capacity is 0, so I’m not sure, that there are some benefits here. Probably the main benefit is that actor is stick to single thread context and there are not switches. What do you think? Thanks for the sharing anyway
g

gildor

04/04/2021, 3:26 PM
Yes, it's correct, it doesn't switch thread context, just having essentially a queue
d

Dmytro Serdiuk

04/04/2021, 3:27 PM
thanks!now it’s clear. Very appreciate for your help!
g

gildor

04/04/2021, 3:28 PM
Actor is just a convinience wrapper around channel + coroutine, so you correct, it's the same solution as actor
d

Dmytro Serdiuk

04/04/2021, 3:29 PM
thank you!!
I have a short question - is it a hard operation, to make a switch for different context(that probably involve switch execution to distinct thread)? Because it’s the only case, why this method is faster, than with locks. As I already checked, Continuation encapsulate the local state of the coroutine and it could be easily resumed on the new thread, why it should have the impact on the performance? Anyway, very thanks for your time
Also, have you pointed about thread switching context or continuation context?Becase for example for Dispatchers.Default you will only have as many thread as cores and there would be no impact of “thread context switch”
g

gildor

04/05/2021, 12:59 AM
I'm talking about thread switch context
Yes, thread switch is relatively heavy operation
d

Dmytro Serdiuk

04/05/2021, 9:47 AM
I know, that I already took a lot of your time, but if you could explain me, why we will end up in thread switch context?
val mutex = Mutex()
var counter = 0

fun main() = runBlocking {
    withContext(Dispatchers.Default) {
        massiveRun {
            // protect each increment with lock
            mutex.withLock {
                counter++
            }
        }
    }
    println("Counter = $counter")
}
In this case we have Dispatchers.Default that are bound to CPU threads, so the counter will be incremented in different threads, but it doesn’t mean that there would be a context switch, as we are bound to CPU cores. In case of actors, there would be the same workers in Dispatchers.Default + one more coroutine in main dispatchers that will be as a queue. Am I right or could you give me more details please. Anyway, thank you
Could you please check my question if you would have some time, please)
g

gildor

04/08/2021, 7:38 AM
This doesn’t cause context switch (it may, becuse it runs on dispatcher with multiple threads, but not necessary)
d

Dmytro Serdiuk

04/08/2021, 9:33 PM
Thank you very much!