GlobalScope vs CoroutineScope()

이태훈·2022년 3월 6일

안녕하세요. 이번에는 GlobalScope와 CoroutineScope의 차이점에 대해 알아보겠습니다.

포스팅에 앞서, CoroutineContext에 대해 어느정도 이해를 가지고 보시는 것을 추천드립니다.

먼저, GlobalScope와 CoroutineScope()의 코드를 한번 보겠습니다.

public object GlobalScope : CoroutineScope {
    /**
     * Returns [EmptyCoroutineContext].
     */
    override val coroutineContext: CoroutineContext
        get() = EmptyCoroutineContext
}

public fun CoroutineScope(context: CoroutineContext): CoroutineScope =
    ContextScope(if (context[Job] != null) context else context + Job())

GlobalScope는 EmptyCoroutineContext를 CoroutineContext로 사용하고 있고, CoroutineScope()는 CoroutineContext가 강제되고, 넘겨 받은 CoroutineContext에 Job이 없으면 추가해주는 것을 볼 수 있습니다.

public object EmptyCoroutineContext : CoroutineContext, Serializable {
    private const val serialVersionUID: Long = 0
    private fun readResolve(): Any = EmptyCoroutineContext

    public override fun <E : Element> get(key: Key<E>): E? = null
    public override fun <R> fold(initial: R, operation: (R, Element) -> R): R = initial
    public override fun plus(context: CoroutineContext): CoroutineContext = context
    public override fun minusKey(key: Key<*>): CoroutineContext = this
    public override fun hashCode(): Int = 0
    public override fun toString(): String = "EmptyCoroutineContext"
}

internal class ContextScope(context: CoroutineContext) : CoroutineScope {
    override val coroutineContext: CoroutineContext = context
    // CoroutineScope is used intentionally for user-friendly representation
    override fun toString(): String = "CoroutineScope(coroutineContext=$coroutineContext)"
}

readResolve() 함수에 대해서는 [Kotlin] Signleton Serialization 을 보시면 됩니다.
간략하게는, Signleton Object가 재생성이 되는 것을 방지하는 함수입니다.

EmptryCoroutineContext를 보면 CoroutineContext의 Element가 아무것도 없는 것을 볼 수 있습니다.

반면에, ContextScope는 파라미터로 넘겨받은 CoroutineContext를 사용하는 것을 볼 수 있죠.

여기서, GlobalScope와 CoroutineScope()는 GlobalScope가 Signleton인 점, CoroutineContext의 Element 유무 말고는 차이점이 없는 것을 볼 수 있습니다.

먼저, GlobalScope가 Signleton이기 때문에 생기는 차이점에 대해 알아보겠습니다.

  • GlobalScope는 Signleton이기 때문에 Application이 살아있는 동안은 계속 살아있습니다.
  • CoroutineScope()는 객체를 생성하기 때문에 호출한 객체가 메모리에서 해제가 되면 같이 파괴가 됩니다. 하지만, 원하는 대로 작동하려면 적절하게 cancel을 호출해줘야 해당 코루틴 작업이 취소됩니다.

다음으로, CoroutineContext의 유무 때문에 생기는 차이점에 대해 알아보겠습니다.

  • Dispatcher : EmptyCoroutineContext로 코루틴 람다를 실행하면 기본적으로 Dispatchers.Default를 지정해줍니다. 그래서, GlobalScope.launch {} 를 하면 Dispatcher.Default로 지정되어 코루틴 람다를 실행하게 되고, CoroutineScope는 생성할 때 넘겨준 Dispatcher로 실행하게 됩니다.
  • Job : GlobalScope는 Job이 없는 CoroutineContext를 가지고 있기 때문에, GlobalScope.cancel() 이런 식으로 코루틴 작업을 취소할 수 없습니다. 따라서, GlobalScope.launch 에서 반환되는 job을 가지고만 취소를 해야합니다. 반대로, CoroutineScope()는 생성할 때 Job이 있는지 체크하여 없으면 Job을 넣어주기 때문에, 바로 cancel이 가능합니다.
profile
https://www.linkedin.com/in/%ED%83%9C%ED%9B%88-%EC%9D%B4-7b9563237

0개의 댓글