코루틴

하승진·2024년 7월 30일
0
post-thumbnail

Intro

var result = 0
for (i in 1..10) {
	result += i
}
println(result)

이와 같은 일반적인 구문들은 동기적(순차적)으로 처리됨을 알 수 있다.
그렇다면 코틀린에서는 비동기처리가 불가능한가? 아니다. 그래서 우리는 코루틴을 배워야 한다.

코루틴(coroutine)

: 메인인 메인루틴과 별도로 진행이 가능한 코루틴으로 개발자가 루틴에 실행과 종료를 마음대로 제어할 수 있는 단위

import kotlinx.coroutines.*

코루틴을 사용하기 위한 패키지 import 구문이다.

코루틴 scope

: 코루틴은 제어범위 및 실행범위를 지정 가능하다.
: 스코프는 GlobalScope와 CoroutineScope 2가지가 있다.

GlobalScope

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

CoroutineScope

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

Dispatcher

  • Dispatchers.Default : 기본적인 백그라운드 동작
  • Dispatchers.IO : I/O에 최적화 된 동작
  • Dispatchers.Main : 메인(UI) 스레드에 동작

** Dispatcher는 모든 플랫폼에서 지원이 아니기에 지원되는 플랫폼에 따라 사용

코루틴 사용

val scope = CoroutineScope(Dispatcher.Default)
val coroutineA = scope.launch {}
val coroutineB = scope.async {}

이거와 같이 코루틴 생성에는 launch와 async 함수가 있다.
그리고 이 둘의 차이는 반환값 여부이다.

  • launch: 반환값이 없는 Job객체
launch {
	for (i in 1..10) {
    	println(i)
    }
}
  • async: 반환값이 있는 Deffered객체
async {
	var sum = 0
    for (i in 1..10) {
    	sum++
    }
    sum
}

이렇듯 반환이 있느냐 없느냐에서 둘을 다르게 사용해야 한다.

하지만 직접 GlobalScope 타입으로 launch함수를 통해 출력하게끔 했는데, 이상하게도 출력이 되지 않는다.

why?

=> 코루틴은 제어되는 스코프 또는 프로그램 전체가 종료되면 같이 종료가 된다. 코루틴이 끝까지 실행되는 것을 보장하려면 일정한 범위에서 코루틴이 모두 실행될 때까지 잠시 기다려줘야 한다.
그래서 방금과 같이 루틴의 경우에서는 main 단 하나이기 때문에 프로세스가 거의 실행 즉시 종료되므로 코루틴도 동작되지 못한 것이다.

so how?

runBlocking {
	launch {}
    // 또는
    async {}
}

이런 식으로 runBlocking을 만들고, 그 안에 launch 또는 async을 만들면 코루틴이 종료할 때까지 메인 루틴을 잠시 대기시켜 준다.

  • 하지만 안드로이드의 경우 메인에 runBlocking 걸게 되면 일정 시간 이상 응답이 없는 경우, ANR(Application Not Responding, 응답 없음)이 발생하며 앱이 강제 종료된다.

어쨌든 이렇게 runBlocking으로 코루틴이 끝날때까지 기다려줌으로써 실행이 된다.

루틴의 대기

delay(milisecond: Long)

: milisecond 단위로 루틴을 잠시 대기시키는 함수

join()

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

await()

: Deffered.await()으로 Deffered의 실행이 끝날때까지 대기하는 함수
: Deffered객체의 결과도 반환

=> 이 3개는 코루틴 내부 또는 runBlocking과 같은 루틴의 대기가 가능한 구문 안에서만 동작이 가능

  1. async대기 출력 (이때 a는 delay로 기다리는중)
  2. b의 기다림
  3. a의 반복문으로 1 출력(b의 기다림)
  4. launch대기 출력
  5. a가 끝날때까지 기다림 (나머지 2, 3, 4, 5 출력)
  6. launch 종료 출력

이런 순으로 진행되는 것이다.

코루틴 중단

cancel() : 코루틴에 cancel()을 걸어주면 2가지 조건이 발생하며 코루틴을 중단 가능

조건

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

이와같이 원래는 join으로 반복 출력이 끝날때까지 기다렸다면 지금은 cancel로 수동으로 종료했기에 2부터 출력없이 바로 종료됨을 알 수 있다.

결과 반환

withTimeoutOrNull() : 제한시간 내에 수행되면 결과값을, 아닌 경우 null을 반환

withTimeoutOrNull (50) {
	for (i in 1..1000) {
    	println(i)
        delay(10)
    }
    "Finish"
}

이런 형태로 제한시간 50ms을 준 상태에서 1부터 1000까지 10ms뒤에 다 출력하게 된다면 "Finish"를 아니라면 null을 반환하도록 하는 것이다.
이 함수도 join, await처럼 blocking 함수이다.

따라서 이와 같이 runBlocking안에 실행을 해야하며 50ms 동안 1부터 5까지의 출력을 10ms 후에 시작하도록 한다. 그렇다면 출력문에 의해 1 2 3 까지는 출력하다가 50ms가 다 지나게 되어 결국 "Finish"가 아니라 null로 반환하게 된다.

profile
기어갈지언정 한 발자국씩이라도 가보자

0개의 댓글