Confused by suspend function in coroutines and thread

37 Views Asked by At

In Android, I usually see some code snippet of suspend functions like this:

viewModelScope.launch { //implemented by Person1
    A()
}

//implemented by Person2
suspend fun A() {
    B()
}

//implemented by Person3
suspend fun B() {
    C()
}

//implemented by Person4
suspend fun C() {
    doSomething()
}

If Person3 may write like below to let C() running on the different thread

suspend fun B() {
    withContext(Dispatchers.IO) {
        C()
    }
}

What is the Person2 who is responsible for A() also think since he is calling suspend function B, so he need to let B running on a thread.

This may create multiple threads which is not necessary, so how to let the Person1 and Person2 know the suspend function he calls is already running on the different thread.

1

There are 1 best solutions below

2
Leviathan On

Let's assume doSomething accesses the file system. It's then the responsibility of that function to move those parts on the IO dispatcher. The function could look like this:

suspend fun doSomething() = withContext(Dispatchers.IO) {
    // accesses the file system here
}

The caller of doSomething, function C, does not need to know about the file system access and which dispatcher the function is executed on. It can simply call doSomething() and everything is fine.

Now, let's assume B decides to call C like this:

withContext(Dispatchers.IO) {
    C()
}

The current coroutine will be moved from the current thread to a thread of the IO dispatcher and C is called. C then calls doSomething and that function now also executes withContext(Dispatchers.IO). But the coroutine already is on that dispatcher, so no additional thread switches happen. Then, the file system access occurs. On a thread of the IO dispatcher, as B aswell as doSomething made sure of.

withContext doesn't create a new coroutine that would occupy a new thread. Instead, the current coroutine is configured in a way that it runs on the appropriate thread - but if it already is running on the correct thread, nothing changes, no new threads are occupied. You can call withContext(Dispatchers.IO) any number of times, only the first one actually has an effect.

This is different from launching a new coroutine with launch or async. That may very well lead to the dispatcher creating a new thread (or reusing one from the thread pool). But that's what suspend functions are for: When you already are in a suspend function, no new coroutine is needed to call the next suspend function. No new threads are involved.

To conclude, it is not necessary that B switches to the IO dispatcher, but it also does no harm. But the documentation of doSomething and C could confirm that the internals make sure the file system access is moved to the IO dispatcher. That way B wouldn't have to guess if everything is handled correctly and if B should, just to make sure, move the current coroutine to the IO dispatcher itself.