Search code examples
kotlinkotlinx.coroutines

Correct way to set kotlin coroutine scope for backend app


What is the correct way to set coroutine scope -

1.Implementing scope

@Service
class MyServiceImpl : MyService, CoroutineScope {
    private val job: Job = Job()
    override val coroutineContext: CoroutineContext
        get() = job + Executors.newFixedThreadPool(100).asCoroutineDispatcher()
    override fun get(): String {
        launch {....}
        return "Result"
    }}

` 2. Without implementing

@Service
class MyServiceImpl : MyService {
    private val scope = Executors.newFixedThreadPool(100).asCoroutineDispatcher()
    override fun get(): String {
        GlobalScope.launch(scope) {....}
        return "Result"
    }}

Or just use GlobalScope without any context??


Solution

  • There is no correct way, all three variants are useful in different scenarios.

    1. Implementing an own coroutine scope: If the lifetime of your coroutines depends on the lifetime of another object. Especially useful in UI applications, e.g. Android, if you need to destroy all pending coroutines, when the starting activity is destroyed, see https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md#structured-concurrency-lifecycle-and-coroutine-parent-child-hierarchy.
    2. Create a scope from a thread pool: If your coroutines should run in a common thread pool. In this scenario the lifetime of a new coroutine is not depending from this scope, but from the outer coroutine:

      val fixedThreadPoolContext = newFixedThreadPoolContext(100, "background")
      launch(Dispatchers.Main) {
          withContext(fixedThreadPoolContext) {
              //parent job is from the outer coroutine
          }
          //or
          val asyncResult = async(fixedThreadPoolContext) {
              //parent job is from the outer coroutine
          }
      }
      
    3. Use global scope: If you like to start some background coroutines, which are not depending on any other object. Typically is it better to provide an own scope that is bound to your application object or some global singleton. Then you can provide your own exception handler, etc

    For a backend service, I would use an own global threadpool dispatcher Then you have the control over the size. Be aware, that GlobalScope uses the CPU count as a parameter to define the pool size. That's great for CPU bounded tasks, but not for IO tasks, like database access.