Kotlin/Coroutine

[Coroutine] 4. Dispatcher과 launch, async를 이용해 Coroutine 붙이기

반응형

앞선 글까지 Dispatcher을 만드는 과정을 거쳤다. 이제부터 본격적으로 Coroutine을 만들기 시작할 것이다.

 

Dispatcher에 Coroutine 붙이기

Dispatcher에 Coroutine을 붙이는 작업은 launch{ }와 aync{ } 두가지 메서드를 통해 가능하다.결과 반환이 없는 단순 작업에는 launch를 결과 반환이 필요한 작업에는 async를 사용한다. 

  결과 반환 반환타입
launch X Job
async O Defered<T>

 

결과를 반환하지 않는 launch

launch는 결과를 반환하지 않고 launch 수행 시 job이 반환된다.

with(CoroutineScope(Dispatchers.Main)) {
    val job: Job = launch { println(1) }
}

 

결과를 반환하는 async

async는 결과를 반환하며 결과값은 Deferred로 감싸서 반환된다. Deferred는 미래에 올 수 있는 값을 담아놓을 수 있는 객체이다.

 

아래 예제에는 async 블록의 마지막 줄에 있는 1이 반환되어야 하므로 Deferred<Int>값이 반환되었다.

CoroutineScope(Dispatchers.Main).launch {
    val deferredInt: Deferred<Int> = async {
        println(1)
        1 // 마지막 줄 반환
    }
    val value = deferredInt.await()
    println(value) // 1 출력
}

Deferred<T>의 await()메서드가 수행되면 코루틴은 결과가 반환되기까지 기다린다. 우리는 이를 코루틴이 일시 중단 되었다고 한다. 이러한 특성으로 인해 await() 메서드는 일시 중단이 가능한 코루틴 내부에서만 사용이 가능하다. 만약 바깥에서 await을 사용하면 fun을 suspend fun(일시중단 함수)로 만들라는 오류가 생긴다.

 

 이후 결과가 반환되었을 때, 코루틴은 다시 재개되고, 위의 예제의 println가 수행되어 1이 출력된다.

 

 


 위에서 두가지 방식을 통해 디스패처에 코루틴을 붙이는 기본적인 방법을 설명하였다. 아래에서는 Dispatcher을 전환하면서 코루틴을 붙이도록 하여 각 코루틴의 작업 성격에 맞게 Dispatcher을 설정하는 방법을 설명할 것이다.

 

Dispatcher 전환하면서 Coroutine 붙이기

파일 시스템으로부터 Array<Int>를 받아와서 정렬한다음 텍스트뷰에 출력하는 과정을 한다고 생각해보자. 이 과정에는 파일 입출력(Dispatchers.IO), Array 정렬(Dispatchers.Default), 텍스트 뷰 출력(Dispatchers.Main) 과 같이 여러 Dispatcher에 맞는 작업이 들어가는데 코루틴은 이러한 Dispatcher Switching을 하기 위한 간편한 방법을 제공한다. 아래에서 살펴보도록 하자.

 

코드는 아래와 같아진다.

CoroutineScope(Dispatchers.Main).launch { // 0. Main Dispatcher를 기본으로 설정

    // 1. 데이터 입출력을 해야 하므로 IO Dispatcher에 배분
    val deferredInt: Deferred<Array<Int>> = async(Dispatchers.IO) { 
        println(1)
        arrayOf(3, 1, 2, 4, 5) // 마지막 줄 반환
    }
    
    // 2. Sort해야 하므로 CPU작업을 많이 해야하는 Default Dispatcher에 배분
    val sortedDeferred = async(Dispatchers.Default) { 
        val value = deferredInt.await()
        value.sortedBy { it }
    }

    // 3. 설정하지 않으면 기본 Dispatcher인 Main Dispatcher에 보내진다. 
    // 3. TextView에 세팅하는 것은 UI 작업이므로 Main Dispatcher에 배분
    val textViewSettingJob = launch {  
        val sortedArray = sortedDeferred.await()
        setTextView(sortedArray)
    }
}

0. 기본적으로 Main Dispatcher 안에서 시작되도록 CoroutineScope(Dispatchers.Main).launch가 수행된다.

 

1. 파일 시스템으로부터 [3, 1, 2, 4, 5] Array를 가져오는 작업은 결과를 반환 받아야 하는 작업이다. 따라서 async를 사용한다. 이 때 파일 입출력이므로 전용 디스패처인 Dispatchers.IO 를 사용한다.

 

아래와 같이 async뒤에 Dispatchers.IO 를 붙임으로써 dispatcher 스위칭이 가능해진다.

async(Dispatchers.IO) { // 데이터 입출력을 해야 하므로 IO Dispatcher에 배분
	..
}

 

2. 이제 Sorting을 해야한다. Sorting은 CPU작업을 많이 사용하므로 Dispatchers.Default에서 수행되어야 한다. 따라서 결과를 반환받을 수 있도록 async를 사용하며 Dispatcher은 Default로 세팅한다. 이때 내부에서 파일 입출력이 끝나기 까지 수행을 일시정지하기 위해 deferredInt.await() 을 사용한다.

async(Dispatchers.Default) { 
	val value = deferredInt.await() // 결과값이 오기를 기다려야 함
	..
}

 

3. 마지막 작업은 텍스트뷰에 결과값을 세팅하는 작업이다. 이 작업은 UI작업이므로 Dispatchers.Main에서 수행되어야 하는데 0번 과정에서 세팅한 Dispatchers.Main이 기본 Dispatcher가 되므로 별도로 Dispatcher세팅 없이 launch{} 만 수행하면 된다.

Sorting된 값이 올때까지 기다려야 하므로 sortedDeferred.await()을 수행하며, 해당 값이 왔을 때 텍스트뷰에 세팅한다.

launch {  //기본 디스패처인 Dispatchers.Main에서 수행됨
	val sortedArray = sortedDeferred.await() // 결과값이 오기까지 기다리기
	setTextView(sortedArray) // textView에 세팅하기
}

 

정리

위에서 Dispatcher에 코루틴을 붙이는 다양한 방법을 살펴보았다. 이를 정리하면 다음과 같다.

  • 결과 반환이 필요 없을 때는 launch를 필요할 때는 async를 사용한다.
  • 코루틴을 생성할 때 Dispatcher을 설정하는 것만으로 Dispatcher의 전환이 가능하다. 
반응형