2024. 2. 7. 22:13ㆍ코틀린
오늘은 그토록 내가 어려워했던 코루틴에 대해서 다시 적어보려고 한다.
이걸 정리하는 이 순간에도 정확히 이해하진 않았지만 계속 보다보면 알겠지,, 싶기 때문에
ㅎㅎ 다시 한 번 읽어본다 ㅋㅋ 파이티이이잉!
코루린이란?
스레드와 달리 코틀린은 코루틴을 통해 복잡성을 줄이고도 손쉽게 일시 중단하거나 다시 시작하는 루틴을 만들어 낼 수 있다. 멀티태스킹을 실현하면서 가벼운 스레드라고도 불린다. 또한 이는 문맥 교환없이 해당 루틴을 일시 중단을 통해 제어한다. 일명 Stackless로 스택을 가지지 않으므로 생성 오버헤드가 줄어든다.
참고!!
스레드 = (동시성 프로그램을 제공하는 기법)
문맥 교환 = (CPU가 어떤 내용을 실행하기 위해서 기존 내용을 저장했다가 다시 내용을 복구하는 작업을 뜻한다.)
코루틴의 주요 패키지를 간단하게 살펴보면,
kotlinx.coroutines의 common 패키지
기능 | 설명 |
launch / async | 코루틴 빌더 |
Job / Deferred | cancellation 지원을 위한 기능 |
Dispatchers | Default는 백그라운드 코루틴을 위한 것이고 Main은 Android나 Swing, JavaFx를 위해 사용 |
delay / yield | 상위 레벨 지연(suspending) 함수 |
Channel / Mutex | 통신과 동기화를 위한 기능 |
coroutineScope / supervisorScope | 범위 빌더 |
select | 표현식 지원 |
Job은 생명주기를 가지고 동작하는 작업의 단위를 뜻한다.
Deferred은 실행된 결과물을 꺼내다가 처리하는 것을 말한다.
Dispatchers은 문맥을 가지고 해당 문맥이 어떤 형태로 작동할지 정의하는 일종의 스케줄러이다.
core 패키지
기능 | 설명 |
CommonPool | 코루틴 컨텍스트 |
produce / actor | 코루틴 빌더 |
이런식의 패키지로 구성되어 있다고 한다.
이를 하나하나씩 따로 정리를 해보자면,
launch
로켓처럼 일단 실행하고 잊어버리는 (fire - and - forget) 형태의 코루틴으로 메인 프로그램과 독립되어 실행 할 수 있다.
기본적으로 즉시 실행하며 블록 내의 실행 결과는 반환하지 않는다. 상위 코드를 블록 시키지 않고(논블로킹) 관리를 위한 Job 객체를 즉시 반환한다. join을 통해 상위 코드가 종료되지 않고 완료를 기다리게 할 수 있다.
async
비동기 호출을 위해 만든 코루틴으로 결과나 예외를 반환한다.
실행 결과는 Deffered<T>를 통해 반환하며 await을 통해 받을 수 있다.
await은 작업이 완료될 때까지 기다리게 된다.
여기서 잠깐!
작업에 앞서서 코루틴과 관련된 라이브러리를 다운로드해야한다.
kotlinx-coroutines-core를 검색하고 가장 최신에 나온 것을 검색해주어야 한다.
전에도 최신버전을 하지 않은 상태로 했더니 알 수 없는 에러가 생겨서 잘 안된 적이 있었다 ㅋㅋㅋ
기본적인 launch 빌더의 사용
fun main() { // 메인스레드의 문맥
GlobalScope.launch { // 새로운 코루틴을 백그라운드에 실행
delay(1000L) // 1초의 논블로킹 지연 (시간의 기본 단위는 ms)
println("world!") // 지연 후 출력
}
println("Hello") // main 스레드가 코루틴이 지연되는 동안 계속 실행
Thread.sleep(2000L) // main 스레드가 jvm에서 바로 종료되지 않고 2초 기다린다.
}
메인에 진입하게 되면 메인 스레드가 돌고 있고 launch를 만난 후로는 또 하나의 코루틴이 생성이 된다.
즉, 총 2개가 running 이라고 보면 된다.
그리고 사용자가 의도적으로 지연(suspend) 함수를 사용해 일시 중단도 가능하다.
이때, sleep는 2초를 기다리게 하지만 메인 호출 스레드를 블록시킨다.
GlobalScope로 지정된 launch 코루틴 수행의 완료를 기다리지 않으면 프로그램 종료로 생성되어 있던 코루틴도 같이 사라지게 된다고 한다... 코루틴의 생명주기는 프로그램 전역에 있기 때문,,
즉, 만일 sleep가 없다면 Hello만 찍히고 world는 찍히지도 않은 채 종료가 된다.
다만, 이는 스레드처럼 스택을 독립으로 가진 요소는 아니라 스레드와 비슷한 개념으로서 루틴을 하나 생성되어 실행된다고 보면 된다.
일시 중단(suspended) 함수
delay()의 경우 일시 중단될 수 있으며 필요한 경우 재개(resume) 된다.
delay()는 '아무런 일을 하지 않는 일' 을 한다는 것은 일시 중단 되어 있는 사이에 다른 루틴을 블록킹 하지 않으므로 다른 일을 할 수 있게 하는 것이라고 한다.
지연 함수는 반드시 코루틴 안에서만 사용해야 한다.
delay() 의 선언부
public suspend fun delay (timeMillis: kotiln.Long): kotlin.Unit {compiled code}
suspend 함수를 코루틴 블록 외에 사용하면 오류가 난다.
사용자 함수의 suspend 적용
suspend fun doSomething() {
println("Do something")
}
컴파일러는 suspend가 붙은 함수를 자동적으로 추출해 Continuation 클래스부터 분리된 루틴을 만든다. 이러한 함수를 사용하기 위해 코루틴 빌더인 launch와 async에서 이용할 수 있다.
Job 객체
코루틴의 생명주기를 관리하며 생성된 코루틴 작업들은 부모 - 자식 간의 관계를 가질 수 있다. 부모가 취소되거나 실행 실패하면 그 하위 자식들은 모두 취소된다. 자식의 실패는 그 부모에 전달되며 부모 또한 실패한다. 다른 모든 자식 역시 취소된다.
supervisorJob
자식의 실패가 그 부모나 다른 자식에 전달되지 않으므로 실행을 유지할 수 있다.
join() 결과 기다리기
Job 객체의 join()을 사용해 완료를 기다릴 수 있다.
launch에서 반환 값을 받으면 Job 객체가 되기 때문에 이것을 이용해 main 메서드에서 join()을 호출 할 수 있다.
fun main() = runBlocking<Unit> {
val job = launch { // Job의 객체를 반환
delay(1000L)
println("world!")
}
println("Hello")
job.join() // 명시적으로 코루틴이 완료되길 기다린다. 취소 할 경우 job.cancel()을 사용한다.
}
이때, job.join은 suspend 함수로 만들어졌기 때문에 main 함수도 코루틴 빌더로 만들어줘야 한다.
그래서 runBlocking(= 코루틴의 블록을 생성하는 것)을 사용했는데, 내부 코드가 완료가 되지 않았으면 코드 밖으로 나가지 않고 블로킹을 취해서 기다리게 한다.
즉, launch가 완료될때까지 상위 루틴을 블록킹 하는 것이다..
Job의 상태
상태 | isActive | isCompleted | isCancelled |
New | false | false | false |
Active (기본값 상태) | true | false | false |
Completing | true | false | false |
Cancelling | false | false | true |
Cancelled(최종 상태) | false | true | true |
Completed(최종 상태) | false | true | false |
완료 단계에서 부모 - 자식 간의 관계가 있을 경우에는 join을 통해 완료를 기다릴 수 있다(with children). 그렇지 않은 경우에는 바로 finish로 넘어가게 된다.
코루틴의 중단과 취소
중단(코루틴 코드 내에서)
delay(시간값) - 일정 시간을 지연( = 블로킹이 아니라 논블로킹으로 내부적으로 실행되는 상태다. )하며 중단
yield() - 특정 값을 산출하기 위해 중단
참고 !! 반면에 Thread Sleep 상태는 블로킹 한 상태이다. 완전히 중단한 상태이므로 주의해야 할 필요가 있다.
취소(코루틴 외부에서)
Job.cancel() - 지정된 코루틴 작업을 즉시 취소된다.
Job.cancelAndJoin() - 지정된 코루틴 작업을 취소된다. (완료시까지 기다린다.)
기본적으로 부모 자식 관계에 적용될 수 있으며 부모 블록이 취소되면 모든 자식 코루틴이 취소된다.
정해진 문맥에 따라서 자식이 만약에 취소가 된다면 부모에게 전파가 된다. 그러면 부모 자체에도 취소시키고 그에 따른 자식들도 다 취소가 된다고 한다. 이를 무관하게 만들려면 supervisorJob을 만들어줘야 한다!
다음 편에도 계속,,,
'코틀린' 카테고리의 다른 글
TIL 20240209 - 코루틴(coroutine) ? (3) (0) | 2024.02.09 |
---|---|
TIL 20240208 - 코루틴(coroutine) ? (2) (0) | 2024.02.09 |
TIL 20240205 - 동시성 프로그래밍 (0) | 2024.02.05 |
TIL 20240204 - 컬렉션의 확장 함수 (0) | 2024.02.04 |
TIL 20240202 - Set과 Map (0) | 2024.02.02 |