When I set Dispatchers.Main for the runBlocking, it got locked. The body of runBlocking is not executing, why?
`runBlocking(Dispatchers.Main){
Log.d(TAG,Thread.currentThread().name)
}
Thank you.
When I set Dispatchers.Main for the runBlocking, it got locked. The body of runBlocking is not executing, why?
`runBlocking(Dispatchers.Main){
Log.d(TAG,Thread.currentThread().name)
}
Thank you.
I guess this is because you execute runBlocking()
from the main thread, so you are blocking it and at the same time you want to schedule something on it - deadlock. You should try to avoid runBlocking()
if possible. What’s your case, what do you try to achieve?
@broot Thank you. I am just trying to understand the dispatchers. As the default dispatcher of a coroutine is main so if I explicitly specified the dispatcher as Dispatchers.Main then why it behaves differently as shown in the question?
OR: Consider the following secenario.
As the default dispatcher (in android) of a coroutine is main. So, if I explicitly specified the dispatcher as Dispatchers.Main then the behavior should be the same. But it is not. Consider the following code.
// Code 1
lifecycleScope.launch {
Log.d(TAG, “A”)
}
Log.d(TAG, “B”)
Output: A, B
//Code 2
lifecycleScope.launch(Dispatchers.Main) {
Log.d(TAG, “A”)
}
Log.d(TAG, “B”)
Output: B, A
I’m not Android developer, but I doubt default dispatcher is ever Main
. For most cases the default is well Dispatchers.Default
, so dispatcher optimized for CPU-intensive workload. From the Android perspective it could be considered a “background worker” dispatcher. And the runBlocking()
is very specific in this matter, it is one of very few exceptions of the above. By default it creates its own dispatcher, using the thread that invoked runBlocking()
. This dispatcher has no representation in Dispatchers.*
.
edit:
Well, what I said is not entirely true. Dispatchers.Default
is default for coroutines framework, but every scope can use its own dispatcher. I suspect you are correct and lifecycleScope
is configured to use Dispatchers.Main
.
I can’t fully explain your case, but I suspect it has something to do with Dispatchers.Main.immediate
dispatcher. Probably in the first case it uses immediate Main
, so it doesn’t at all schedule the code block, but executes it directly. In the second case you asked for Dispachers.Main
, so this is like explicitly asking for scheduling the code block on Main
. Try to use Dispatchers.Main.immediate
instead of Dispatchers.Main
.
@broot Thank you for valuable time. Yes I have tried Dispatchers.Main.immediate. Then A is printing before B.
Let me re-phrase the question.
Are lifecycleScope.launch {} (default dispatcher is Main) and lifecycleScope.launch(Dispatchers.Main) {}
(dispatcher is explicitly specified as Main) are the same ???
Why don’t you log the thread name and/or the identity hash code in both situations? That should tell you which dispatcher is actually running your coroutine.
I have tried that, both prints main. as shown in the following example.
lifecycleScope.launch{
Log.d(TAG, "Default dispatcher: " + Thread.currentThread().name)
}lifecycleScope.launch (Dispatchers.Main){
Log.d(TAG, "Explicit dispatcher: " + Thread.currentThread().name)
}
Output:
D/LearningCoroutines: Default dispatcher: main
D/LearningCoroutines: Explicit dispatcher: main
I believe they are almost the same. lifecycleScope.launch(Dispatchers.Main) {}
adds the code block to the queue of Dispatchers.Main
. And lifecycleScope.launch {}
which I guess is effectively lifecycleScope.launch(Dispatchers.Main.immediate) {}
checks if it is running inside Main
already and in that case it executes the code block directly. If it is invoked outside of Main
then it adds to the queue, similarly to the first option.
Both cases use the same main thread, but immediate
uses some kind of optimization to not schedule the code block if it could be executed directly.
which I guess is effectively `lifecycleScope.launch(Dispatchers.Main.immediate) {} here you are right. the default dispatcher of lifecycleScope is Dispatchers.Main.immediate as mentioned here. Before I was thinking it is just Dispatchers.Main.
That’s why A is printing before B in the following example.
lifecycleScope.launch {
Log.d(TAG, “A”)
}
lifecycleScope.launch(Dispatchers.Main) {
Log.d(TAG, “B”)
}
The things got cleared to some extent. But still having some confusion in original question that I asked that-is why the following code get locked.
runBlocking(Dispatchers.Main){
Log.d(TAG,Thread.currentThread().name)
}
@broot You answered the following
Here my point is that deadlock occurred when at least two processes racing for the same resource. But there is only one process runBlocking{}, I am just specifying the dispatcher explicitly.
as you answered
in the response of
I think this should be true for runBlocking{} case as well, that is runBlocking{} and runBlocking(Dispatchers.Main) {} behave the same way
I do not know that I communicated what I exactly want. Please, bear with me. Thank you
Ok, let me explain this more extensively. runBlocking()
works by “hijacking” the thread that invoked it. From the caller perspective this thread is constantly blocked (as the name suggest) until all the code inside runBlocking()
finishes executing. If you execute runBlocking()
from the main thread, that means you block this thread (at least from the perspective of the outside world, because internally it still could do something). Main looper is blocked, Dispatchers.Main
is blocked, runOnUiThread()
won’t work, etc.
Now, if you use runBlocking(Dispatchers.Main) {}
then runBlocking()
blocks the main thread, then schedules executing the code block using Dispatchers.Main
, but… Dispatchers.Main
can’t execute it, because main thread is blocked. runBlocking()
waits for Dispatchers.Main
and Dispatchers.Main
waits for runBlocking()
- this is a deadlock.
As said above, default dispatcher for runBlocking()
is a very specific one. It doesn’t use any dispatchers from Dispatchers.*
, but it “hijacks” the thread and creates its own internal dispatcher using this thread.
Why don’t you log the thread name and/or the identity hash code in both situations? That should tell you which dispatcher is actually running your coroutine.
Threads and dispatchers are very different things, though.
Knowing something executes on the main thread doesn’t mean it has been executed on the Main
dispatcher. It could have been Dispatchers.Main
, Dispatchers.Main.immediate
, the event loop of a runBlocking
, or even nothing related to coroutines at all.
I know. It was just the first step for the analysis.