[Android] 코루틴

곽호택·2021년 11월 18일
0

안드로이드

목록 보기
13/16
post-thumbnail

! 스레드 저번에 스레드 공부를 한 이유가 코루틴을 공부하기 위해서인데 혹시 스레드에 대해 모르신다면 저기 링크나 다른 블로그에서 찾아보시는 것을 추천드려요(모르고 공부하니까 진짜 모르겠음...)

1. 코루틴?

코루틴이 나오기 이전에 앱이나 웹에서 비동기 처리를 위해 rx programming을 많이 사용해왔다. 하지만 구글이 안드로이드 공식 언어를 코틀린으로 변경한 이후에 코루틴에 대한 중요도도 상당히 높아졌다고 생각된다.

쾌락코딩, Kotlin Coroutine 번역 이 두 블로그를 보면서 공부를 했다.

코루틴은 스레드 안에서 실행되는 일시 중단 가능한 작업의 단위로 하나의 스레드에 여러 코루틴이 존재할 수 있다.

코루틴은 resume 될 때 마다 다른 스레드에서 실행될 수 있고, 특정 스레드에서만 국한될 수 있다.

코루틴의 특징 중 중요한 부분이 이 두가지 라고 한다.

  • 협력형 멀티 태스킹
  • 동시성 프로그래밍 지원

1) 협력형 멀티태스킹

코루틴(Co + Routine)에서 Co는 "협력"이라는 의미를 지니고 있고, Routine은 하나의 함수라고 생각하면 된다. 직역하자면 "협력하는 함수"이다.

Routine에 대해 간단히 알아보자면 main routine과 sub routine이 존재한다.

fun main(){
    ...
    val beerCount = drinkBeer(value)
    ...
}

fun drinkBeer(value : Int){
    val one = 1
    val beerCount = vlaue + one
    
    return beerCount
}

다음과 같은 코틀린 코드에서 main 함수는 메인이 되는 함수!

여기서 drinkBeer라는 서브 함수를 호출한다.

메인 함수는 메인 루틴, 서브 함수는 서브 루틴이 된다.

이 서브 루틴의 경우 루틴에 진입하는 곳과 빠져나오는 곳이 명확하게 보인다.

메인 루틴이 서브 루틴을 호출했을 때, 서브루틴의 맨 처음에 진입하여 return을 호출하거나 닫는 괄호를 만날 때 이 서브 루틴을 빠져나오게 된다.

위의 코드를 예시로 들자면, drinkBeer 함수에 진입하고 return을 통해 빠져나오게 된다.(명확해!)

그러나 코루틴은 다르다.

일단 코루틴도 routine이므로 하나의 함수로 여기자.

위의 서브 루틴과 다르게 코루틴은 진입할 수 있는 곳도 여러 개이고, 함수를 빠져나갈 수 있는 곳도 여러개다.

즉 중간에 나갈 수 있고, 다시 나갔던 그 지점으로 들어올 수도 있다.


이때 나갈 수 있도록 하는 코드가 suspend 코드이다.

추가로 이 suspend를 붙이는 것은 코틀린 컴파일러에게 이 함수가 코루틴 안에서 실행되어야 한다고 알려주는 역할!!

코드로 보자

val scope = CoroutineScope(Dispatchers.Main)

fun homeWork(){
	scope.launch{
    	writeCode()
        writeReadMe()
        gitCommit()
        gitPush()
    }
}
suspend fun writeCode(){
    delay(2000)
}

suspend fun wrtieReadMe(){
    delay(3000)
}

suspend fun gitCommit(){
    delay(5000)
}

suspend fun gitPush(){
    delay(1500)
}

homeWork라는 함수가 있다. 쓰레드의 main 함수가 이 homeWork라는 함수를 호출하면 우리가 만든 코루틴 스코프가 launch라는 코루틴 빌더를 만나서 코루틴을 만들어줍니다.(나중에 따로 설명)

위에서 말했다시피 이 homeWork()는 언제든지 진입할 수 있고 탈출할 수 있게 된다.

코루틴이 실행되고, suspend 키워드의 함수를 만나게 되면, (여기서는 writeCode에 해당) 더 이상 그 아래의 코드를 실행하지 않고 homeWork() 라는 코루틴 함수를 탈출한다!

탈출했더라도 메인 쓰레드는 가만히 있지 않고, 다른 코드들을 실행하거나 UI 작업을 진행할 수도 있다. 그렇지만 어디선가 writeCode는 계속 실행되고 있다.(다른 쓰레드에서 실행될 수도 있고, 동시성 프로그래밍으로 작동될 수도 있음)

메인쓰레드가 다른 코드를 실행하고 있다가도, wirteCode() 가 다 실행되었을 때 다시 탈출했던 homeWork() 코루틴으로 돌아오고, 그 아래인 writeReadMe()부터 다시 resume 된다.

정리하면 위의 그림과 같다.

2) 동시성 프로그래밍

  • 동시성 프로그래밍 : 여러가지 작업이 동시에 처리되는 것

  • 병렬 프로그래밍 : 정말로 동시에 같이 일어나는 것


동시성 프로그래밍과 병력 프로그래밍은 보기에는 같은 개념인 것 같은데 알고 보면 정말 다르다.

위의 코드에 썼던 과제의 예시를 들어보자!

먼저 동시성 프로그래밍의 경우

그림과 같이 노트북 한개에 모니터 두개를 연결한 모습이다!

이때 왼쪽 모니터에는 안드로이드 스튜디오가, 오른쪽 모니터에는 ReadMe가 켜져 있다.

안드로이드 스튜디오에서 코드를 작성하다가, 재빠르게 오른쪽 모니터로 눈을 옮겨 ReadMe를 작성한다. 그리고 이러한 행동을 엄청 아주 빠르게 반복한다!!

반면, 병렬성 프로그래밍은

노트북 두개를 왼손으로는 코드 작성, 오른 손으로 ReadMe를 작성하는 것이다.

A
val scope = CoroutineScope(Dispatchers.Main)

fun homeWork(){
	scope.launch{
    	writeCode()
        writeReadMe()
        gitCommit()
        gitPush()
    }
}
suspend fun writeCode(){
    delay(2000)
}

suspend fun wrtieReadMe(){
    delay(3000)
}
B
val scope = CoroutineScope(Dispatchers.Main)

fun homeWork(){
	scope.launch{
    	writeCode()
        writeReadMe()
        gitCommit()
        gitPush()
    }
}
suspend fun writeCode(){
    delay(2000)
}

suspend fun wrtieReadMe(){
    delay(3000)
}

코루틴도 루틴이기 때문에 하나의 쓰레드에 여러개가 존재 가능하다.

위의 코드에서 메인 쓰레드 안에 코루틴이 두개가 있다. 하나는 왼쪽 모니터, 또 하나는 오른쪽 모니터라고 생각하면 편하다.

왼쪽 코루틴에서 suspend 키워드의 함수를 만나 빠져 나와 다른 코루틴안의 suspend 함수를 만나게 되면, 이때 쓰레드 하나에서 동시성 프로그래밍이 가능해진다!

2. 코루틴 사용

코루틴에서 주로 사용하는 키워드들만 간단히 보자면 다음과 같다!

  • CoroutineScope

  • CoroutineContext

  • Dispatcher

  • launch & async

하나하나씩 살펴보자

1) CoroutineScope

말 그대로 코루틴의 범위로 코루틴 블록을 묶음으로 제어할 수 있는 단위

이 스코프에서 생성된 코루틴을 계속해서 주시하면서 실행을 취소하거나, 실패할 시에 예외를 처리할 수 있게 해준다.

단, GlobalScope도 쓸 수 있지만 코루틴의 생명주기를 제어하기를 원한다면 권장하지 않는다!


val scope = CoroutineScope(Dispatchers.Main)

fun homeWork(){
	scope.launch{
    }

코루틴의 실행을 멈추기 위해서 cancel()메소드를 활용한다.

scope.cancel()

2) CoroutineContext

코루틴을 어떻게 처리할 것 인지에 대한 여러가지 정보의 집합

CoroutineContext의 주요 요소로 Job과 Dispathcer가 있다.

Job에 대한 내용은 이 분의 블로그를 참고 Taehwan
하여 공부할 예정

3) Dispathcer

어떤 스레드를 이용해서 어떻게 동작할 것인지를 정의

  • IO : 네트워크나 디스크 작업 할 때 사용

  • Defalut : CPU 사용량이 많은 작업, 주 스레드에서 작업하기에 너무 긴 작업 들

  • Main : UI 작업이나, 쓰레드를 블락하지 않고 빨리 실행되는 작업에 사용

val scope = CoroutineScope(Dispathcer.IO)

4) launch & async

생성된 스코프에서 launch와 async를 사용하여 코루틴을 생성할 수 있다.

  • launch의 경우에는 반환값이 없으며(job), 단순히 그 자리에서 실행시키고 끝남!

  • async는 반환값이 존재한다.(Deffered)

이 둘의 공통점은

  • 새로운 코루틴을 만든다.

  • 하나의 Dispatcher를 가진다.

  • 스코프 안에서 실행된다.

  • suspend 함수가 아니다.

3. 코루틴 사용법

코루틴 사용 단계는 다음과 같다.

  1. 사용할 Dispatcher를 결정

  2. Dispatcher를 이용해서 CoroutineScope를 만들고

  3. CoroutineScope의 launch 또는 async에 수행할 코드 블록을 넘겨준다.

profile
잘하고싶다

0개의 댓글