코틀린 강좌 #30[완] 코루틴을 통한 비동기 처리

Kotlin/기본 문법 · 2020. 12. 24. 17:51

테크과학! DiMo

 

테크과학! DiMo

개발자가 얘기하는 생활 속 IT기술에 대한 과학! 여러분이 몰랐던, 혹은 정확히 알지 못했던 IT 테크놀러지를 파드립니다! 좋은 영상을 위한 채널 후원, 투네이션을 통해서 해주세요! 후원링크 htt

www.youtube.com

 

 

개발 환경: play.kotlinlang.org



코루틴(coroutine)

 


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

코루틴을 사용할 때는 import kotlinx.coroutines.* 하여야 한다

 

 


코루틴의 scope


코루틴은 제어 범위 및 실행 범위를 지정할 수 있다.

 


- GlobalScope

 

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

 


- CoroutineScope

 

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

 

 

CoroutineScope를 만들 때 적용 가능한 Dispatcher

 

 

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

 

이러한 Dispatcher들은 모든 플랫폼에서 지원되지는 않음. 따라서 지원되는 플랫폼에 따라서 사용해야 한다.

 

 

코루틴은 이러한 Scope에서 제어되도록 생성될 수 있다.

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

 

 

launch vs async

 

반환 값이 있는지 여부!

 


launch : 반환 값이 없는 Job 객체

 


async : 반환 값이 있는 Deffered 객체

 

 

 

 

 

launch, async 모두 람다 함수의 형태를 가지고 있기 때문에 그렇기 때문에 async는 마지막 구문의 실행 결과(그림에서는

 

sum)가 반환된다.

 

import kotlinx.coroutines.*

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

Output:
실행x(라고 설명하는데, 내 인터넷 환경에서는 1 2 3 4 5(줄바꿈 없이 출력될 때도 있고, 줄바꿈 되서 출력 될 때도 있었다.)

 

 

코루틴은 제어되는 스코프 또는 프로그램 전체가 종료되면 함께 종료되기 때문에 코루틴이 끝까지 실행되는 것을 보장

 

하려면 일정한 범위에서 코루틴이 모두 실행될 때 까지 잠시 기다려야 한다.

 

우리가 테스트 하는 루틴의 경우 main() 함수 단 하나이기 때문에 프로세스가 거의 '실행 즉시 종료' 되므로 코루틴도 동작되지 못한 것.


이럴 때는 runBlocking{ }을 만들고 이 안에서 launch{}나 async{} 직접 생성하면 코루틴이 종료될 때까지 메인 루틴을 잠

 

시 대기 시켜준다.

주의할 점은 안드로이드에서는 메인 스레드에서 runBlocking을 걸어주면 일정 시간 이상 응답이 없는 경우

 

ANR(Application Not Responding)이 발생하여 앱이 강제 종료 된다.

 

 

import kotlinx.coroutines.*

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

Output:
1
2
3
4
5

 

 

루틴의 대기를 위한 추가적인 함수

delay()
delay(millisecond:Long) millisecond 단위로 루틴을 잠시 대기시키는 함수

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

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

 

또한 await()는 Deferred의 결과도 반환함

 

세 함수들은 코루틴 내부 또는 runBlocking{}과 같은 루틴의 대기가 가능한 구문 안에서만 동작이 가능하다.

 

 

 

import kotlinx.coroutines.*

fun main() {
   runBlocking {
       val a = 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 종료")
   }
}

Output:
async 대기
1
async 종료
launch 대기
2
3
4
5
launch 종료

 

 

코루틴에 cancel()을 걸어주면 다음 두 가지 조건이 발생하며 코루틴을 중단 시킬 수 있다.

 


1. 코루틴 내부의 delay() 함수 또는 yield() 함수가 사용된 위치까지 수행된 뒤 종료됨


2. cancel()로 인해 속성인 isActive가 false가 되므로 이를 확인하여 수동으로 종료함

 

 

import kotlinx.coroutines.*

fun main() {
   runBlocking {
       val a = launch {
           for (i in 1..5) {
               println(i)
               delay(10)
           }
       }
       val b = async {
           "async 종료"
       }
       
       println("async 대기")
       println(b.await())
       
       println("launch 취소")
       a.cancel()
       println("launch 종료")
   }
}

Output:
async 대기
1
async 종료
launch 취소
launch 종료

 

 

withTimeoutOrNull()

 


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

 

이 함수도 join()이나 await()처럼 blocking 함수이다.

 

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

Output:
1
2
3
4
null

 

 

 

 

완강 소감

 

이 강의로 코틀린 문법을 입문한 것은 다행이라고 생각한다. 필요한 부분을 짧고 쉽게 설명해주셔서 쉽게 

 

비교적 쉽게 완강할 수 있었다. 다만 실행 환경을 웹상에서 진행하였기 때문에 실제 IDE(인텔리제이)에서의

 

숙련도는 오르지 않아, 인텔리제이에서 사용이 익숙해지도록 인텔리제이에서도 코틀린 코드 작성을 해봐야

 

할 것 같다. 이 글을 보는 사람이 있을지 모르겠지만, 만약 코틀린 문법을 입문한다면 이 강의로 시작해 봐

 

도 좋을 것 같다는 생각이 든다.

 

이 강의의 제작자인 디모 님에게 감사의 인사 올린다.