Devik0213 / memo

메모
1 stars 0 forks source link

coRoutine #8

Open Devik0213 opened 6 years ago

Devik0213 commented 6 years ago

코루틴이란

caller가 함수를 call하고, 함수가 caller에게 값을 return하면서 종료하는 것에 더해 return하는 대신 suspend(혹은 yield)하면 caller가 나중에 resume하여 중단된 지점부터 실행을 이어갈 수 있다. 참고

얼마전 구글 I/O 코틀린 디자인 세션에서도 소개되었습니다. 아래는 해당 영상입니다.(맨 뒷부분에서 코루틴에 대한 이야기가 나옵니다.) https://youtu.be/6P20npkvcb8

영상을 보시면 발표자는 기존 Thread를 사용하는 것보다 가볍고 콜백지옥(?)으로 부터 벗어 날수 있다고 이야기 합니다.

Android에서 코루틴 적용 방법

우선 gradle에서 코루틴 라이브러리를 적용해줍니다.

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"

android용 코루틴 라이브러리가 별도로 존재하며, 현재 까지의 버전은 '0.22.5'입니다. 그리고 gradle.properties에서

kotlin.coroutines=enable

다음과 같이 적용해 줍니다.

코루틴의 비동기 처리

@Test
    fun simpleCoroutinTest() {
        for (i in 0..10000) {
            //(Runtime.getRuntime().availableProcessors() - 1).coerceAtLeast(1)
//            async(CommonPool)
            async {
                println("thread id : ${Thread.currentThread().id}")
            }
        }
    }

위와 같이 async와 람다를 사용하여 적용이 가능하며 CommonPool이라는 default 매개변수를 생략 가능합니다. CommonPool은 현재 가능한 프로세서 및 최소 1개 이상의 thread pool을 제공합니다.

만일 고정적인 쓰레드의 풀을 사용하고자 한다면, 아래와 같이 별도의 pool을 만들수 있습니다.

@Test
    fun customPoolCoroutineTest() {
        val pool = newFixedThreadPoolContext(1, "background")
        for (i in 0..10000) {
            async(pool) {
                println("thread id : ${Thread.currentThread().id}")
            }
        }
    }

많은 문서에서 코루틴의 비동기 처리는 기존 Thread 방식보다 훨씬 가볍고 심지어 No context switch이라 고도 이야기 합니다. 하지만, 이 부분은 위험요소가 있기 때문에 기존 처럼 heap이나 data 메모리에 대해 동기화처리를 하는게 좋을것 같습니다. 위의 테스트를 돌려보시면 서로 다른 쓰레드의 id가 나오는것을 아실수 있습니다. 그리고 동일한 코루틴간에만 안전하며 서로 다른 코루틴간에는 위험하다고 합니다. (https://github.com/Kotlin/kotlin-coroutines/blob/master/kotlin-coroutines-informal.md)

코루틴문서 스택오버플로우

비동기 결과 값 반영

대부분은 비동기 처리 후에 결과를 UI에 반영하기 위해 main thread(main looper)에 메시지를 던지고 콜백을 구현하여 view에 접근합니다. 그런데 이러한 방식은 view의 hierarchy가 깊어질수록 콜백 구현이 많아지고 코드의 가독성이 떨어지게 됩니다.

아래와 같이 코루틴의 lauch & async & await를 사용하면 콜백 없이 구현이 가능합니다.

@Test
    fun launchCorutineTest() {
//        launch(UI)
        launch {
            val result = async {
                Thread.sleep(1000)
                return@async "result"
            }.await()
            println("this is main thread, result: $result")
        }

        Thread.sleep(2000)
    }

default 매개변수인 UI변수 를 생략가능하며 UI변수는

val UI = HandlerContext(Handler(Looper.getMainLooper()), "UI")

위와 같이 main looper입니다.

await사용시 해당 부분에서 중지 되었다가 async의 결과가 끝나면 중지된 다음 부분에서 실행이 됩니다.

코루틴의 비동기를 사용하지 않고 다른 비동기 처리에서 결과 같은 반영하고자 한다면, suspendCoroutine를 사용하면 됩니다.

@Test
    fun asyncTaskWithCoroutineTest() {
        launch {
            var result = suspendCoroutine<String> {
                it.resume("result")
            }
            println("this is main thread, result: $result")
        }

        Thread.sleep(2000)
    }

위와 같이 resume으로 결과 값을 반영 할수 있습니다.

아직 버전 1점대(정식 버전)가 아니며, rxjava와 retrofit, glide등의 라이브러리를 이용하여 비동기처리 작업을하고 있어 코루틴의 비동기 처리 작업을 사용할 일은 많지 않을것 같습니다. 하지만, suspendCoroutine을 사용한다면 다른 비동기 라이브러리와 적용함으로서 코드의 가독성은 높아질것으로 보입니다.