[Kotlin] Kotlin 플레이그라운드의 코루틴 소개

June·2023년 10월 10일

Kotlin 플레이그라운드의 코루틴 소개

  • 코루틴에서 '코-'는 함께 작동함을 의미
  • 코루틴에서 '-루틴' 부분은 함수와 같은 명령의 세트를 의미

동기

동기 코드, 함수

  • 동기 코드는 한 번에 하나의 작업을 처리하며, 각 작업이 완료된 후에 다음 작업을 실행
  • 함수 호출이 동기식이므로 함수 호출이 완료된 후에 다음 코드로 진행

runBlocking()

  • runBlocking()은 비동기적인 작업을 동기적으로 실행할 수 있도록 도와주는 함수
  • runBlocking()을 사용하여 비동기 작업을 동기적으로 실행
  • runBlocking()은 학습 및 테스트 목적으로 사용
  • Android는 메인 스레드에서 UI 및 사용자 이벤트를 처리하므로 메인 스레드를 블록하면 앱의 응답성이 저하

정지 함수

  • 정지 함수는 일시 중지되었다가 다시 시작할 수 있는 함수로, 코루틴 내에서 호출

코루틴과 동시성

  • 코루틴을 사용하면 여러 작업을 동시에 실행할 수 있으며, 각 작업은 지연 함수를 만나면 중지되고 다른 작업이 실행

실행 시간 측정

  • measureTimeMillis() 함수를 사용하여 코드 블록을 실행하는 데 걸리는 시간을 측정

코드

import kotlin.system.*
import kotlinx.coroutines.*

fun main() {
    val time = measureTimeMillis {
        runBlocking {
            println("Weather forecast")
            printForecast()
            printTemperature()
        }
    }
    println("Execution time: ${time / 1000.0} seconds")
}
suspend fun printForecast() {
    delay(1000)
    println("Sunny")
}

suspend fun printTemperature() {
    delay(1000)
    println("30\u00b0C")
}

출력

Weather forecast
Sunny
30°C
Execution time: 2.145 seconds



비동기

launch() 함수를 사용한 동시 실행

  • Kotlin의 코루틴은 구조화된 동시 실행이라는 핵심 개념
  • launch() 함수를 사용하여 새 코루틴을 실행하고 동시에 여러 작업을 실행할 수 있다

코드

import kotlin.system.*
import kotlinx.coroutines.*

fun main() {
    val time = measureTimeMillis {
        runBlocking {
            println("Weather forecast")
            launch {
                printForecast()
            }
            launch {
                printTemperature()
            }
        }
    }
    println("Execution time: ${time / 1000.0} seconds")
}

suspend fun printForecast() {
    delay(1000)
    println("Sunny")
}

suspend fun printTemperature() {
    delay(1000)
    println("30\u00b0C")
}
  • 메인 스레드를 차단하지 않고 비동기 작업을 수행하여 이전 동기코드와 비교했을 때 출력 결과는 동일하지만 더 빠른 출력을 보여준다

출력

Weather forecast
Sunny
30°C
Execution time: 1.178 seconds


async() 함수를 사용한 비동기 작업 처리

  • async() 함수를 사용하여 비동기 작업을 처리하고 각 작업의 결과를 Deferred 객체로 반환

  • await() 함수를 사용하여 각 작업의 결과에 접근하고 이를 조합하여 출력

코드

import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis

fun main() {
    val time = measureTimeMillis {
        runBlocking {
            println("Weather forecast")
            val forecast: Deferred<String> = async {
                getForecast()
            }
            val temperature: Deferred<String> = async {
                getTemperature()
            }
            println("${forecast.await()} ${temperature.await()}")
            println("Have a good day!")
        }
    }
    println("Execution time: ${time / 1000.0} seconds")
}

suspend fun getForecast(): String {
    delay(1000)
    return "Sunny"
}

suspend fun getTemperature(): String {
    delay(1000)
    return "30\u00b0C"
}

출력

Weather forecast
Sunny 30°C
Have a good day!
Execution time: 1.195 seconds

병렬 작업 처리 coroutineScope

  • coroutineScope 함수를 사용하여 코루틴의 범위를 만들어 여러 동시 작업을 구조화하고 병렬로 실행
  • 여러 작업을 병렬로 수행한 후 그 결과를 결합하여 출력하는 방법
  • coroutineScope()는 실행된 코루틴을 포함한 모든 작업이 완료된 후에만 반환

코드

package coroutinescope

import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis

fun main() {
    val time = measureTimeMillis {
        runBlocking {
            println("Weather forecast")
            println(getWeatherReport())
            println("Have a good day!")
        }
    }
    println("Execution time: ${time / 1000.0} seconds")
}

suspend fun getWeatherReport() = coroutineScope {
    val forecast = async { getForecast() }
    val temperature = async { getTemperature() }
    "${forecast.await()} ${temperature.await()}"
}

suspend fun getForecast(): String {
    delay(1000)
    return "Sunny"
}

suspend fun getTemperature(): String {
    delay(1000)
    return "30\u00b0C"
}

출력

Weather forecast
Sunny 30°C
Have a good day!
Execution time: 1.184 seconds



예외 및 취소

예외

  • 예외는 예상치 못한 이벤트로, 코드 실행 중에 발생하며, 앱이 비정상 종료되지 않도록 적절한 예외 처리를 구현

코루틴 예외

  • 코루틴에서 예외가 발생하면 해당 예외가 계층 구조를 따라 상위 코루틴으로 전파
  • 상위 코루틴에서 예외를 처리하지 않으면 최상위 루트까지 전파되어 앱 전체가 비정상 종료될 수 있다
  • try-catch 블록을 사용하여 코루틴 내에서 예외를 처리

코드

package exception

import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis

fun main() {
    val time = measureTimeMillis {
        runBlocking {
            println("Weather forecast")
            println(getWeatherReport())
            println("Have a good day!")
        }
    }
    println("Execution time: ${time / 1000.0} seconds")
}

suspend fun getWeatherReport() = coroutineScope {
    val forecast = async { getForecast() }
    val temperature = async {
        try {
            getTemperature()
        } catch (e: AssertionError) {
            println("Caught exception $e")
            "{ No temperature found }"
        }
    }

    "${forecast.await()} ${temperature.await()}"
}

suspend fun getForecast(): String {
    delay(1000)
    return "Sunny"
}

suspend fun getTemperature(): String {
    delay(500)
    // 코드 실행 중에 예상치 못한 상황이 발생했을음 나타내는 오류
    throw AssertionError("Temperature is invalid")
    return "30\u00b0C"
}

출력

Weather forecast
Caught exception java.lang.AssertionError: Temperature is invalid
Sunny { No temperature found }
Have a good day!
Execution time: 1.219 seconds

코루틴 취소

  • 코루틴은 상위 코루틴이나 외부 요인에 의해 취소될 수 있다
  • 코루틴은 협력적으로 취소되어야 하며, 상위 코루틴의 취소는 하위 코루틴에 영향을 주지 않는다
  • cancel() 함수를 사용하여 코루틴을 취소할 수 있다

코루틴 개념

CoroutineScope

  • 코루틴은 일반적으로 CoroutineScope 내에서 실행되며, 이를 통해 코루틴의 범위와 수명 주기를 관리하여 리소스 낭비를 방지
  • launch()async()CoroutineScope의 확장 함수

CoroutineContext

  • CoroutineContext는 코루틴이 실행될 컨텍스트에 관한 정보를 제공하며, 이름, 작업, 디스패처, 예외 핸들러 등의 요소를 포함
  • 이름(코루틴을 고유하게 식별), 작업(코루틴의 수명 주기 제어), 디스패처(작업을 적절한 스레드에 전달), 예외 핸들러(예외처리)

Dispatchers

  • Dispatchers는 코루틴이 실행될 스레드를 결정하는 역할을
  • 기본적으로 Main, IO, Default 디스패처가 제공
  • 작업의 특성에 맞게 선택하여 사용

    Dispatchers.Main

    • 기본 Android 스레드에서 코루틴 실행
    • UI 업데이트, 상호작용에 주로 사용

    Dispatchers.IO

    • 기본 스레드 외부에서 디스크 또는 네트워크 I/O 실행
    • 파일 읽기/쓰기, 네트워크 작업에 최적화

    Dispatchers.Default

    • 컨텍스트에 디스패처 지정되지 않은 경우의 기본 디스패처
    • 계산이 많은 작업을 기본 스레드 외부에서 실행에 사용
profile
끝까지 해보자

0개의 댓글