-
Notifications
You must be signed in to change notification settings - Fork 1.9k
[Question] Is there thread switching when moving from Default dispatcher to IO dispatcher using withContext? #3234
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Hi, the documentations stays the following:
It is rather an explanation of implementation details than any sort of guarantee. |
@qwwdfsad, hi! I think this is an opportunity to improve the docs. This is the complete sentence:
Even though the text after "—" uses the typically qualifier, the text before is adamant that the dispatcher does not lead to an actual switching to another thread. In my opinion, a better phrasing would be:
Maybe some additional clarification of when the user shall expect context switching or not would also be nice. |
@qwwdfsad One more question for my specific case. |
@psteiger thanks for the suggestion, I've reworded the documentation! @fanliver yes, in general it is a good practice to do all I/O on a pool separate from a regular threads doing CPU-related work. Though I do not have any strong recommendations here -- it's your application, you know its business and performance characteristics better and the final decision should be driven by the actual application-specific knowledge. |
@qwwdfsad Do I understand correctly that switching from edit: |
There are, because coroutine-specific dispatching mechanism and dispatcher/executor/scheduler-specific context switching and task distribution (between threads) are not coupled in any way and do not know about each other.
That's exactly the reason I do not want to write any additional information in the documentation :) The scheduler is optimized for applications in steady state, not unit tests that are being executed in Java interpreter, where the cost of stack spilling (what coroutine code actually does prior to getting a chance to execute To illustrate that, see the following, modified example:
with that, we now have much better and much more consistent output: |
@qwwdfsad
Ah, ok, by seeing that we can use dispatchers/continuation interceptors to hook into the process of both creating continuations and resuming them, I somehow suspected that dispatchers have quite a lot of control and understanding over the whole process. I was thinking about something similar to (just an example): object DefaultIoScheduler {
fun isDispatchNeeded(context: CoroutineContext): Boolean {
return callerDispatcher != Dispatchers.IO && callerDispatcher != Dispatchers.Default
}
} Where
Is my understanding more or less correct?
Ohh, wow, so the answer to the whole discussion is basically that OPs and mine expectations were correct, but we verified results in a wrong way :-) That's great explanation! |
It will affect a lot of code in a pretty unforeseeable way. For example the following code
will now become sequential and will take 10 seconds instead of 1. Dispatching and context switching are fundamentally different -- dispatching controls whether something should be dispatched (executed not in place) at all, while context switching is an optimization that does not force anything, but rather nudges implementation to do more efficient choices when it's possible: if the dispatching thread became dormant, it is hinted to take recently scheduled by this very thread task. For better understanding, one can read http://gee.cs.oswego.edu/dl/papers/fj.pdf as an example of a good design for similar (but not the same though) goals. The fact, that this is an optimization that guarantees liveness (e.g. that the code in the former example will be executed in 1 sec nevertheless) makes it extremely hard and timing-sensitive. I think following example may shed more light on the difference between dispatching choice and context switching optimization:
In scenario "1", the launching thread immediately suspends in its In scenario "2", the optimization sees that the thread is still "busy" (blocked) and thus dynamically allocated/hands the task to another thread in IO dispatcher. Dispatching cannot distinguish these two situations statically at the moment of |
According to the documents of
Dispatchers.IO
, there will be no thread switching when usingwithContext
to switch fromDefault
dispatcher toIO
dispatcher becauseIO
dispatcher share the thread pool withDefault
dispatcher.However, when I run my test code, the result doesn't seem to be like that.
Here is the code:
And this is the result:
As you can see, every coroutine
start
in one thread andcontinue
in another thread.Could anyone explain why?
Or is there anything wrong with my code?
The text was updated successfully, but these errors were encountered: