Android(kotlin) - Coroutine(1)

하동혁 ·2023년 6월 16일
0

Android

목록 보기
4/4
post-thumbnail

루틴이란?

  • 컴퓨터 프로그램에서 하나의 정리된 일(작업을 정의한 명령어의 집합)
  • 프로그램은 보통 크고 작은 여러가지 루틴을 조합시켜 성립된다.
  • 루틴은 메인 루틴(main routine)과 서브 루틴(subroutine)으로 나뉜다.



서브루틴(subroutine)과 코루틴(coroutine)의 차이점

  1. 코루틴에서는 메인-서브 개념이 없이 모든 루틴들이 서로를 호출 할 수 있다.
  2. 메인루틴에서 특정 서브루틴의 공간으로 이동한 후에 return 의해 호출자로 돌아와 다시 프로세스를 진행하는 서브루틴과는 다르게, 코루틴은 루틴을 진행하는 중간에 멈추어서 특정 위치로 돌아갔다가 다시 원래 위치로 돌아와 나머지 루틴을 수행할 수 있다.
  3. 서브루틴은 진입점과 반환점이 단 하나밖에 없어 메인루틴에 종속적이다. 반면, 코루틴은 진입지점이 여러 개이기 때문에 메인루틴에 종속적이지 않아 대등하게 데이터를 주고 받을 수 있다.



Coroutine (코루틴)

코루틴은 코틀린 뿐 아니라 여러 프로그래밍 언어에서 쓰이는 개념입니다.


Android의 kotlin의 코루틴

  • 코루틴은 비동기적으로 실행되는 코드를 간소화하기 위해 Android에서 사용할 수 있는 동시 실행 설계 패턴입니다.
    코루틴은 kotlin버전 1.3에 추가되었으며 다른 언어에서 확립된 개념을 기반으로 합니다.
  • Android에서 코루틴은 기본 스레드를 차단하여 앱이 응답하지 않게 만들 수도 있는 장기 실행 작업을 관리하는 데 도움이 됩니다.
    코루틴을 사용하는 전문 개발자 중 50% 이상이 생산성이 향상되었다고 보고했습니다.
  • 코루틴은 Android의 비동기 프로그래밍에 권장되는 솔루션입니다.



blocking

Blocking은 어떤 함수가 실행 중일 때 해당 함수가 반환하기 전까지는 다른 작업을 수행할 수 없는 상태를 말합니다

non-blocking

Non-blocking은 어떤 함수가 실행 중일 때에도 다른 작업을 수행할 수 있는 상태를 말합니다.

코루틴에서 blocking 함수를 호출할 경우, 해당 함수의 실행이 완료될 때까지 다른 작업을 수행할 수 없으므로, 코루틴의 실행이 일시 중지되며, 블로킹 상태에 빠지게 됩니다. 반면, non-blocking 함수를 호출할 경우, 해당 함수의 실행이 완료되지 않아도 다른 작업을 수행할 수 있으므로, 코루틴의 실행이 계속됩니다. 이러한 방식으로, 코루틴에서 비동기적으로 여러 작업을 처리할 수 있게 됩니다.



의존성 추가

build.gradle(Module)

dependencies {
 .....
 .....

    //coroutine
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
}

runBlocking{ } 메서드

  • 주어진 블록이 완료될 때까지 현재 스레드를 점유하는 코루틴을 생성하고 실행하는 코루틴 빌더 입니다.
  • 코루틴 안에서 사용은 권장되지 않습니다.
  • 일반적인 함수 코드 블록에서 일시중단 함수를 호출할 수 있도록 하기 위해서 존재합니다.
  • Android UI에서 runBlocking을 잘못 사용하면 화면이 멈추는 현상이 발생할 수 있으므로 주의가 필요합니다.

중단 함수 - delay( )

  • 모든 중단 함수들은 코루틴 안에서만 호출될 수 있습니다.
  • delay를 이용해 blocking을 할 수 있습니다.
  • runBlocking, CoroutineScope, GlobalScope 안에서만 동작합니다.
fun main() = runBlocking {

    GlobalScope.launch {
        delay(1000L)
        println("World!")
    }

    println("Hello,")
    delay(500L)
}

위 코드는 runBlocking 내부에 GlobalScope.launch으로 다른 코루틴을 실행한 코드입니다.
runBlocking{ }으로 현재 스레드를 점유하도록 하여 메인스레드가 코루틴의 작업이 다 끝날때 까지 기다리도록 했습니다.
그러나 GlobalScope.launch를 통해 코루틴을 실행하면 종료를 대기하는 특성이 사라집니다.

GlobalScope는 전역 범위에서 코루틴을 실행하는 범용 범위입니다.(Android Application과 라이프사이클리 동일한 코루틴) 이는 앱의 수명 주기에 종속되지 않으며 독립적으로 실행됩니다. 따라서 코루틴이 완료되지 않더라도 앱이 종료되어도 계속 실행될 수 있습니다.
따라서 위 코드에서 delay(500L) 이후에도 코루틴이 완료될 때까지 프로그램이 종료되지 않습니다.

[결과]
Hello,



fun main() = runBlocking {
    launch {
        delay(1000L)
        println("World!")
    }

    println("Hello,")
}

위 코드는 runBlocking{ } 내부에 launch로 코루틴을 실행했습니다.
launch는 현재 runBlocking 블록과 같은 범위에서 코루틴을 실행하는 범위입니다. 이는 앱의 수명 주기에 종속됩니다.
따라서 코루틴의 작업이 모두 끝나기 전 까지 메인스레드가 종료되지 않습니다.

[결과]
Hello,
World!



suspend function

특징

suspend 함수는 일시 중단되고 다른 작업으로 제어 흐름이 이동할 수 있는 지점이 됩니다. 일반적으로 비동기적인 작업을 수행하는 동안 suspend 함수가 일시 중단됩니다. 이 함수는 완료되기를 기다리는 동안 다른 코루틴이 실행될 수 있도록 허용합니다.
즉, suspend 함수는 코루틴에서 비동기 작업을 간편하게 처리하고 효과적인 동시성을 구현하는 데 유용한 기능입니다.

하위 메서드로 refactoring하여 다른 메서드를 호출할 수 있습니다.

// runBlocking : 현재 코루틴 종료대기. 현재 Thread 점유.
fun main() = runBlocking { // this: CoroutineScope
    launch { doWorld() }
    println("Hello")
}

// this is your first suspending function
suspend fun doWorld() {
    delay(1000L)
    println("World!")
}

[결과]
Hello
World!

주로 언제 사용할까? (예시)

안드로이드에서 네트워크 요청을 수행하는 함수는 일반적으로 suspend 함수로 구현됩니다.
이 함수는 네트워크 요청을 보낸 후 결과를 기다리기 위해 일시 중단되며, 네트워크 작업이 완료되면 해당 지점에서 재개됩니다.

suspend fun fetchUserData(): UserData {
    // 네트워크 요청 보내기
    val response = apiService.getUserData()

    // 응답 처리 및 결과 반환
    return processResponse(response)
}



coroutineScope

  • coroutineScope 빌더는 suspend function내에서 여러 작업을 동시에 수행할 수 있습니다.
  • coroutineScope는 내부 코루틴이 모두 실행이 끝나야 종료합니다.
fun main() = runBlocking {
    doWorld()
    println("Done")
}

// Concurrently executes both sections
suspend fun doWorld() = coroutineScope { // this: CoroutineScope
    launch {
        delay(2000L)
        println("World 2")
    }
    launch {
        delay(1000L)
        println("World 1")
    }
    println("Hello")
}

[결과]
Hello
World 1
World 2
Done


runBlocking vs coroutineScope

  • 공통점: 현재 block을 수행한 후에 다음이 작업이 수행
  • runBlocking은 현재 Thread에서 코루틴을 만들어서 종료까지 join으로 대기하는 방식으로(현재 스레드를 점유), 호출한 Thread가 코루틴 내부에서 대기하는 저 수준의 Blocking.
  • coroutineScope은 async/await 방식으로 대기. 현재 수행중인 Thread는 언제든 탈출했다가 다른 작업을 수행가능하다.
  • 따라서 runBloking은 일반함수이고, coroutineScope는 일시중단함수(suspend function)이다.


launch , async

launch와 async는 coroutineScope 내에서 사용되는 코루틴 빌더입니다. 그러나 두 빌더는 목적과 반환값에 차이가 있습니다.

- launch

launch는 가볍고 비동기적인 작업을 실행하기 위해 사용됩니다. launch를 사용하여 코루틴을 시작하면, 해당 코루틴은 완료 결과를 반환하지 않습니다. 따라서 launch는 실행 결과를 기다리지 않고 즉시 다음 코드로 진행합니다. 즉, launch는 "fire-and-forget" 방식으로 사용되며, 결과에 대한 관심이 없는 경우에 유용합니다.

coroutineScope {
    launch {
        // 비동기 작업 실행
    }
    
    // 다음 코드
}

- async

async는 비동기 작업을 실행하고 해당 작업의 결과를 반환하는 데 사용됩니다. async는 Deferred 객체를 반환하며, 이 객체를 통해 비동기 작업이 완료되면 결과에 접근할 수 있습니다. async는 await() 함수를 사용하여 작업의 결과를 기다릴 수 있습니다. 따라서 async는 작업의 결과를 필요로 하는 경우에 유용합니다.

coroutineScope {
    val deferredResult = async {
        // 비동기 작업 실행
        return "Result"
    }
    
    val result = deferredResult.await() // 작업의 결과를 기다림
    
    // 결과를 사용한 다음 작업
}

0개의 댓글