티스토리 뷰

안드로이드[Kotlin]

[Kotlin]코루틴

JeongeunChoi 2021. 11. 27. 00:04

루틴의 종류

우선 루틴의 종류에 대해 살펴보면 메인루틴, 서브루틴, 코루틴이 있다.

  • 메인루틴: main 함수에 의해서 수행되는 프로그램의 흐름
  • 서브루틴: main 함수 안에서 실행되는 개별함수들에 의해서 수행되는 흐름

즉 함수는 루틴의 한 종류이다!

-> 보통 루틴은 일직선적인 흐름을 가지고 있다.

 


코루틴

  • 특징은 일직선적인 흐름을 중간에 suspend해서 지연을 시켰다가 resume을 통해 다시 재시작하는 것이 가능하다는 점이다.
  • 코루틴을 사용하면 이미 실행이 되었더라도 다른 루틴이 실행되는 동안 잠시 suspend를 시켰다가 바쁜게 끝나고 나면 다시 멈춘 곳에서부터 다시 재시작해서 나머지 작업을 끝내는 비동기 프로그래밍이 가능하게 된다.
  • 코루틴은 스레드가 아니다.

코루틴 구조

Coroutine Scope

  • 코루틴이 어떤 범위에서 동작하는지 규정하는 코루틴 스코프가 정해지면

Coroutine Context

  • 그 안에서는 어떤 스레드에서 이 코루틴을 돌릴지 결정하는 코루틴 컨텍스트를 정해준다. 코루틴 컨텍스트가 정해지면

Coroutine Builder

  • 이 작업을 수행을 하고 값을 리턴을 할지 안할지 그런 부분을 정해주는 코루틴 빌더를 실행하게 된다. 그러면 코루틴 객체가 생기면서 코루틴 작업을 수행할 수 있게 된다.

코루틴 스코프

GlobalScope

  • 앱의 라이프사이클과 함께 동작해서 별도의 생명주기 관리 필요하지 않다.
  • Ex) 앱의 시작부터 종료까지 혹은 장시간 실행되어야 할 때 사용

CoroutineScpoe

  • 작업이 필요할 때만 실행하고 완료되면 종료
  • Ex) 버튼을 클릭해서 서버의 정보를 가져오거나 파일을 여는 용도

Dispatchers: 코루틴이 실행되는 스레드 지정

  • Default(CPU연산을 많이 사용하는 작업에서 사용됨)
  • IO(파일 IO나 네트워크 IO를 많이 사용하는 작업에서 사용됨)
  • Main(UI 스레드 Main 스레드에서 UI 관련 변경을 해야 될때 사용됨)
  • Unconfined(일반적인 용도로는 사용하지 않음)

코루틴 시작

launch

  • 호출 시 코루틴 생성되고 Job 객체 반환, 이 Job을 변수에 저장해두고 상태 관리용으로 사용 가능

async

  • 코루틴 시작하며 Deffered 객체 반환를 반환하므로 코루틴 스코프의 연산 결과를 받아 사용 가능
둘의 차이!
launch는 코루틴 작업시 시작되면 그 안에서 작업을 하고 끝이고
async에서 작업을 하면 작업을 하고 맨 마지막에 남은 숫자를 값을 반환

+ withContext: Dispatcher를 전환시키는 기능을 가짐



Job & Deferred

코틀린에서는 코루틴 작업을 Job 혹은 Deferred라는 object로 만들어서 다루게 된다.

Deferred는 결과값을 가지는 Job으로 사실상 Job으로 생각하면 된다.

코루틴이라는 어떤 그 추상적인 흐름을 Job이라고 하는 어떤 object으로 만들어서 사용한다.

 

코틀린에서는 이렇게 코루틴 한덩어리를 Job이라는 object로 만들게 되면

이 object에서 취소나 예외 처리를 하면 코루틴 흐름 제어 용이하게 할 수 있다.


코루틴 스코프에 대해서 launch 명령어로 코루틴 작업을 만들면 그 작업이 Job이라는 객체를 반환하게 된다.

코루틴은 일시정지 시킬 수 있는 작업의 흐름이기 때문에

Job은 코루틴의 여러 가지 상태를 반영할 수 있도록 여러 가지 상태를 가질 수 있도록 설계가 되어 있다.


Job 관련 메소드들

start

  • 새로 만들어진 객체를 시작을 하게되고 ???

cancel

  • 코루틴 동작을 멈춘다.

join

  • 코루틴의 실행이 완료된 후에 다음 코루틴이 순차적으로 실행되도록 해준다.

Ex) 코루틴스코프 안에 여러개의 launch 블럭들이 있으면 모두 새로운 코루틴으로 분기가 되어 동시에 처리되므로 순서를 정할 수 없다. 이를 순차적으로 실행하기 위해 join 사용

 
CoroutineScope(Dispatchers.Default).launch() {
    launch {
        for(i in 0..5) {
            delay(500)
            Log.d("코루틴", "결과1 = $i")
        }
    }.join()
    launch {
        for(i in 0..5) {
            delay(500)
            Log.d("코루틴", "결과2 = $i")
        }
    }

+ delay: delay에 정해진 시간동안 코루틴 처리가 중단된다.

async로 실행한 코루틴에 대해서는 await 명령어를 사용해서 Job 실행이 끝날때까지 대기를시킬수가있음

특히 await를 사용하면 Deffered의 실행이 끝날때까지 대기를 시키고 이 Deffered 값을 await를 통해서 반환을 하게 된다.

CoroutineScope(Dispatchers.Default).async {
    val deffered1 = async {
        delay(500)
        350
    }
    val deffered2 = async {
        delay(1000)
        200
    }
    Log.d("코루틴", "연산 결과 = ${deffered1.await() + deffered2.await()}")
}
 

suspend

코루틴 안에서 suspend 키워드로 선언된 함수가 호출되면 이전까지의 코드 실행이 멈추게 되고, suspend 함수의 처리가 완료된 후에 멈춰 있던 원래 스코프의 다음 코드가 실행된다.

suspend fun subRoutine() {
    for(i in 0..10) {
Log.d("subRoutine", "$i")
    }
}
CoroutineScope(Dispatchers.Main).launch {
    // (코드 1)
    subRoutine()
    // (코드 2)
}

-> subRoutine은 suspend 키워드를 붙임으로 코루틴스코프안에서 자동으로 백그라운드 스레드처럼 동작

subRoutine()이 실행되면서 호출한 측의 코드를 잠시 멈췄지만 스레드의 중단은 없음!(코루틴의 특징)


runBlocking

runBlocking{} 블록은 주어진 블록이 완료될때까지

현재 스레드를 멈추는 새로운 코루틴을 생성하여 실행하는 코루틴 빌더

코루틴 빌더인 runBlocking 을 사용하면 내부 코루틴이 완료될 때까지 메인 스레드가 blocking되어 프로그램이 중단되지 않는다.

import kotlinx.coroutines.*

fun main(){
    //GlobalScope.launch{} 주어진 코드블록을 수행하는 코틀린을 생성하는 코루틴 빌더
    //해당 코드블록은 코루틴 안에서 수행된다.
    GlobalScope.launch{
        println("World!")
    }
    println("Hello.")
    // 
   	runBlocking{
	// GlobalScope.launch{}로 실행된 코루틴의 수행이 완료될때까지 
	// 현재 스레드(main 함수)를 대기시키기 위해서 임의의 지연을 준다
        delay(2000L)
    }
    // 그런데 내부적으로 실행중인 코루틴이 작업을 완료하고 종료될때까지
    // 얼마나 대기해야 할지 부모 코루틴은 예측할 수 없으므로
    // launch로 실행한 코루틴에 대해서는 join으로 Job의 실행이 끝날때까지 
    // 대기시킬수있음
}

->

 
fun main(args: Array<String>) = runBlocking {
   val job = GlobalScope.launch { 
       delay(1000L)
       println("World!") 
   } 
   println("Hello,") 
   job.join() 
}

launch{} 코루틴 빌더를 이용하여 새로운 코루틴을 생성하면 현재 위치한 부모 코루틴에 join()을 명시적으로 호출할 필요없이 자식 코루틴들을 실행하고 종료될때까지 대기할 수 있다.

fun main(args: Array<String>) = runBlocking { 
   launch { 
       delay(1000L) 
       println("World!") 
} 
   println("Hello,") 
}

예외처리

CoroutineExceptionHandler를 이용하여 코루틴 내부의 기본 catch block으로 사용할 수 있다.


참고

https://flow9.net/bbs/board.php?bo_table=kotlin&wr_id=51

 

40 코루틴 Coroutine > Kotlin | flow9.net - 코틀린, 안드로이드

본문 # 코루틴 Coroutine 코틀린은 백그라운드 스레드(Thread)를 경량화한 코루틴(Coroutine)을 제공합니다. 동시성 프로그래밍이 가능한 코루틴은 다른 언어에서 이미 사용되고 있는 개념을 코틀린에

flow9.net