Coroutine or ThreadPool for non-wait simple task


I used to replace all my async code with coroutine which uses custom thread pool as dispatcher.
Now I’m reviewing my code and thinking about why need to use coroutine on a simple task that don’t wait for another task.
For example:

    GlobalScope.launch(mainThreadHandlerDispatcher) {
        val t = withContext(computationDispatcher) { builder() }
        weakRef.get { text = t }

    submitToComputationPool {
        val t = builder()
        runOnUi {
            weakRef.get { text = t }

I know that coroutine is easy to code and read when complex logical, but what’s the benefit to use coroutine when I can simply use thread pool for non-wait task?


Coroutines (at least default dispatcher on JVM) use custom thread pool under the hood. So in general, both variants will do exactly the same. The difference is that in coroutines you do not have to bother with creating and closing the executor service.


My coroutines use custom thread pools under the hood, because I got problems by using kotlin default common dispatcher. Without consider the lifecycle of thread pool and no await/join/cancel operation between tasks, is there any reason I should use coroutines?


You can even use dedicated thread if the task is run only once. Coroutines are just tools. You should decide yourself whether you want to use it. In my own opinion, program looks more tidy if I use coroutines everywhere.


Thanks, I understand that coroutines are just tools and it’s ok to submit task to thread pool directly instead of coroutines.
I don’t use new thread directly for short-lived task because it can’t be reused. In my opinion, create a coroutine is little heavy for simple task.


What kind of problems?

Why do you ignore all errors in your examples?


I haven’t used common dispatcher for a long time, I guess it’s because of the thread pool is fixed, so short-lived tasks blocked by long-lived tasks in some cases. Now I am using fixed thread pool for computation only and cached thread pool for IO operations, like Schedulers in RxJava.

The examples are not for executes, just for explains it’s a simple async task written in different ways: suspend coroutine vs Runnable.


Wish you proof how much is easy to write example code without coroutine?
So yes, you can use thread pool for tiny, non production code instead of coroutine.


Of couse it can be very easy, like most developers do.

// A global executor for computation, also shared with custom CoroutineDispatcher.
// Should be accessible in same class or module only. This is an example.
val computationExecutor: ExecutorService = Executors.newFixedThreadPool(

inline fun runOnComputationPool(crossinline task: () -> Unit) {
    return computationExecutor.execute { task.invoke() }
    // Example of simple task, no cancel, no join, no callback hell, no...
    runOnComputationPool {
        val result = compute()
        runOnUi {
            // use result here.

Coroutines generates more bytecode to classes, and won’t be faster than execute task directly by executorservice.
I post this is just to confirm that there is no need to use coroutines in this case, because I had replaced all my code with coroutines a year ago and now I’m thinking why. Of couse I prefer coroutines in other cases.