본문 바로가기

카테고리 없음

Kotlin 코루틴(Coroutine)을 이용한 비동기 처리에 대해서 간단히 알아보자!

Coroutine 사용하기

  • import kotlinx.coroutines.*

Coroutine의 특징

  • 제어 범위 및 실행 범위를 지정 할 수 있다. (Scope라 한다)
  • GlobalScope, CoroutineScope를 지원

GlobalScope란?


프로그램 어디서나 제어, 동작이 가능한 기본 범위


CoroutineScope란?


특정한 목적의 Dispatcher를 지정하여 제어 및 동작이 가능한 범위


CoroutineScope 를 만들 때 적용가능한 Dispatcher은?

  • Dispatcher.Default : 기본적인 백그라운드동작
    • 상세 : CPU 사용량이 많은 작업에 사용합니다. 주 스레드에서 작업하기에는 너무 긴 작업 들에게 알맞습니다.
    • 스레드 개수 : Dispatchers.DEFAULT는 forkJoinPool을 내부에서 쓰고 기본 개수는 cpu 코어 개수
  • Dispatcher.IO : I/O에 최적화 된 동작
    • 상세 : 네트워크, 디스크 사용 할때 사용합니다. 파일 읽고, 쓰고, 소켓을 읽고, 쓰고 작업을 멈추는것에 최적화되어 있습니다.
    • 스레드 개수 : Dispatchers.IO는 system property(kotlinx.coroutines.io.parallelism)를 보고 없으면 64개 입니다
  • Dispatcher.Main : 메인(UI) 스레드에서 동작
    • 스레드 개수 : 1개인가?
  • Coroutine 은 Scope에서 제어되도록 생성될 수 있다.
val scope = CoroutineScope(Dispatchers.Default) // 생성된 Scope
val coroutineA = scope.launch{} // launch 함수를 통한 새로운 Coroutine을 생성할 수 있다.
val coroutineB = scope.async{} // async 함수를 통한 새로운 Coroutine을 생성할 수 있다.


launch
반환값이 없는 job 객체

async
반환값이 있는 Deffered 객체

CODE

// 테스트 (1)
import kotlinx.coroutines.*

class CoroutineMain
    fun main() {
        val scope = GlobalScope

        scope.launch {
            for (i in 1..5){
                println(i)
            }
        }
    }

/*
(출력)
Process finished with exit code 0
*/

실행 결과 아무 값도 출력 되지 않는다.

이유는?

코루틴은 제어되는 스코프 또는 프로그램 전체가 종료되면 같이 종료 된다.
코루틴이 실행되는 것을 보장하려면 코루틴을 기다려 주어야 한다.

// 해결 방법
runBlocking {
    launch{}
    async{}
{

runBlocking 을 사용하면 main 루틴을 잠시 대기 시켜준다.
안드로이드에서는 main thread에서 runBlocking을 걸어주고 일정 시간에 응답이 없는경우 (ARN)이 발생함으로 주의한다.

// 테스트 (1)
import kotlinx.coroutines.*

class CoroutineMain
    fun main() {
        val scope = GlobalScope

        runBlocking
            scope.launch {
                for (i in 1..5){
                    println(i)
                }
            }
        }
    }

/*
(출력)
1
2
3
4
5
*/

그럼 루틴의 대기 처리는 어떻게 할 수 있을까



delay() : delay(milisecond:Long)
milisecond 단위로 루틴을 잠시 대기

join() : job.join()
Job의 실행이 끝날때까지 대기하는 함수

awit() : Deferred.await()
Deferred의 실행이 끝날때까지 대기하는 함수

위 함수는 루틴의 대기가 가능한 공간 안에서만 동작이 가능하다
CODE

fun main() {
    val scope = GlobalScope
    runBlocking {
        val a = scope.launch {
            for (i in 1..5){
                println(i)
                delay(10)
            }
        }

        val b = async {
            "async 종료"
        }

        println("async 대기")
        println(b.await())

        println("launch 대기")
        a.join()
        println("launch 종료")
    }
}
/*
(출력)
1
async 대기
async 종료
launch 대기
2
3
4
5
launch 종료
*/

그럼 루틴의 실행 중단 방법은?



cancle() : 함수를 통해 중단 가능

코루틴 내부의 delay() 함수 또는 yield() 함수가 사용된 위치까지 수행된 뒤 종료함
cancle()로 인해 속성인 isActive가 false 되므로 이를 확인하여 수동으로 종료함

CODE

fun main() {
    val scope = GlobalScope
    runBlocking {
        val a = scope.launch {
            for (i in 1..5){
                println(i)
                delay(10)
            }
        }

        val b = async {
            "async 종료"
        }

        println("async 대기")
        println(b.await())

        println("launch 대기")
        a.cancel() //cancel로 변경
        println("launch 종료")
    }
}
/*
1
async 대기
async 종료
launch 취소
launch 종료
*/

withTimeoutOrNull() : 제한 시간내 수행하면 결과값을 아닌경우 null을 반환하는 함수

fun main() {
    val scope = GlobalScope
    runBlocking {
        var result = withTimeoutOrNull(50){
            for(i in 1..10){
                println(i)
                delay(10)
            }
            "Finish"
        }

        println(result)
    }
}
/*
1
2
3
4
null
*/

시간내 수행되지 못해 null이 발생하는 것을 확인 할 수 있습니다.
이상으로 Coroutine을 간단히 알아보았습니다.

링크

디모의 가장쉬운 kotlin : https://www.youtube.com/watch?v=Lpieg1zrKdg&list=PLQdnHjXZyYadiw5aV3p6DwUdXV2bZuhlN&index=31