Kotlin Coroutine에서 일시 중지 기능은 무엇을 의미합니까?


118

Kotlin Coroutine을 읽고 있으며 suspend기능을 기반으로한다는 것을 알고 있습니다. 하지만 무슨 suspend뜻일까요?

코 루틴 또는 함수가 일시 중단됩니까?

에서 https://kotlinlang.org/docs/reference/coroutines.html

기본적으로 코 루틴은 스레드를 차단하지 않고 일시 중단 할 수있는 계산입니다.

사람들이 종종 "기능 중지"라고 말하는 것을 들었습니다. 하지만 함수가 끝날 때까지 기다리기 때문에 일시 중단되는 것은 코 루틴이라고 생각합니다. "suspend"는 일반적으로 "작업 중단"을 의미하며이 경우 코 루틴이 유휴 상태입니다.

🤔 코 루틴이 일시 중단되었다고해야합니까?

어떤 코 루틴이 일시 중단됩니까?

에서 https://kotlinlang.org/docs/reference/coroutines.html

비유를 계속하기 위해 await ()는 일부 계산이 완료되고 결과를 반환 할 때까지 코 루틴을 일시 중단하는 일시 중단 함수 (따라서 비동기 {} 블록 내에서도 호출 가능) 일 수 있습니다.

async { // Here I call it the outer async coroutine
    ...
    // Here I call computation the inner coroutine
    val result = computation.await()
    ...
}

🤔 "계산이 완료 될 때까지 코 루틴을 일시 중단합니다"라고 말하지만 코 루틴은 경량 스레드와 같습니다. 따라서 코 루틴이 일시 중단되면 어떻게 계산을 수행 할 수 있습니까?

우리는 볼 await에 호출 computation이 될 수 있도록, async그 수익률 Deferred은 다른 코 루틴을 시작할 수있는 수단,

fun computation(): Deferred<Boolean> {
    return async {
        true
    }
}

🤔 인용구 는 코 루틴을 중단 한다고 말합니다 . 그것은 suspend외부 async코 루틴을 의미합니까 , 아니면 suspend내부 computation코 루틴을 의미합니까 ?

합니까 suspend평균이 외부 동안 async코 루틴이 (대기 await내부 용) computation마무리에 코 루틴, 그것은 (외부 async코 루틴) 스레드 풀에 아이들링 (그래서 이름이 일시 중단) 반환 스레드, 그리고이 때 아이의 computation코 루틴 완료, 그것은 (외부 async코 루틴을 ) 깨어나고 풀에서 다른 스레드를 가져와 계속합니까?

내가 스레드를 언급 한 이유는 https://kotlinlang.org/docs/tutorials/coroutines-basic-jvm.html 때문입니다.

코 루틴이 대기하는 동안 스레드가 풀로 반환되고 대기가 완료되면 코 루틴이 풀의 사용 가능한 스레드에서 다시 시작됩니다.

답변:


113

일시 중단 함수 는 모든 코 루틴의 중심에 있습니다. 일시 중지 기능은 단순히 일시 중지했다가 나중에 다시 시작할 수있는 기능입니다. 장기 실행 작업을 실행하고 차단없이 완료 될 때까지 기다릴 수 있습니다.

일시 중단 함수의 구문은 suspend키워드 추가를 제외하고 일반 함수의 구문과 유사 합니다. 매개 변수를 취하고 리턴 유형을 가질 수 있습니다. 그러나 일시 중단 함수는 다른 일시 중단 함수 또는 코 루틴 내에서만 호출 할 수 있습니다.

suspend fun backgroundTask(param: Int): Int {
     // long running operation
}

내부적으로 suspend 함수는 컴파일러에 의해 suspend 키워드가없는 다른 함수로 변환되며 유형의 추가 매개 변수를 사용합니다 Continuation<T>. 예를 들어 위의 함수는 컴파일러에 의해 다음과 같이 변환됩니다.

fun backgroundTask(param: Int, callback: Continuation<Int>): Int {
   // long running operation
}

Continuation<T> 반환 값 또는 함수가 일시 중단 된 동안 오류가 발생한 경우 예외와 함께 코 루틴을 재개하기 위해 호출되는 두 개의 함수가 포함 된 인터페이스입니다.

interface Continuation<in T> {
   val context: CoroutineContext
   fun resume(value: T)
   fun resumeWithException(exception: Throwable)
}

4
또 다른 미스터리가 밝혀졌습니다! 큰!
WindRider

16
이 기능이 실제로 어떻게 일시 중지되었는지 궁금합니다. 그들은 항상 suspend fun일시 중지 할 수 있다고 말하지만 정확히 어떻게할까요?
WindRider

2
@WindRider 현재 스레드가 다른 코 루틴을 실행하기 시작하고 나중에이 코 루틴으로 돌아올 것임을 의미합니다.
Joffrey

2
나는 "신비한"메커니즘을 알아 냈다. Tools> Kotlin> Bytecode> Decompile btn의 도움으로 쉽게 공개 할 수 있습니다. 소위 "일시 중지 지점"이 어떻게 구현되는지 보여줍니다. 누구나 자신을 찾을 수 있습니다.
WindRider

4
@buzaa 다음 은 바이트 코드 수준에서 설명하는 Roman Elizarov의 2017 년 강연 입니다.
Marko Topolnik

30

코 루틴을 일시 중단하는 것이 정확히 무엇을 의미하는지 이해하려면 다음 코드를 살펴 보는 것이 좋습니다.

import kotlinx.coroutines.Dispatchers.Unconfined
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

var continuation: Continuation<Int>? = null

fun main() = runBlocking {
    launch(Unconfined) {
        val a = a()
        println("Result is $a")
    }
    10.downTo(0).forEach {
        continuation!!.resume(it)
    }
}

suspend fun a(): Int {
    return b()
}

suspend fun b(): Int {
    while (true) {
        val i = suspendCoroutine<Int> { cont -> continuation = cont }
        if (i == 0) {
            return 0
        }
    }
}

Unconfined코 루틴 발송자의 마법 제거 코 루틴 파견을 우리가 베어 코 루틴에 직접적으로 초점을 맞출 수 있습니다.

launch블록 내부의 코드 는 launch호출 의 일부로 현재 스레드에서 즉시 실행되기 시작 합니다. 다음과 같은 일이 발생합니다.

  1. 평가 val a = a()
  2. 이것은에 연결되어 b()에 도달 suspendCoroutine합니다.
  3. 함수 b()는 전달 된 블록을 실행 한 suspendCoroutine다음 특수 COROUTINE_SUSPENDED값 을 반환 합니다. 이 값은 Kotlin 프로그래밍 모델을 통해 관찰 할 수 없지만 컴파일 된 Java 메서드가 수행하는 작업입니다.
  4. a()이 반환 값을 보는 Function 자체도 반환 값을 반환합니다.
  5. launch블록은 동일하지 및 제어 이제 다음 줄에 반환 launch호출 :10.downTo(0)...

이 시점에서 launch블록 내부의 fun main코드 와 코드가 동시에 실행되는 것과 동일한 효과 가 있습니다. 이 모든 것이 단일 원시 스레드에서 발생하므로 launch블록이 "일시 중지"됩니다.

이제, 내부 forEach루프 코드, 프로그램은 읽어 continuation것을 b()함수가 쓴 resumes의 가치와 함께 10. 전달한 값으로 호출이 반환 resume()되는 것처럼 구현됩니다 suspendCoroutine. 따라서 갑자기 b(). 전달한 값 resume()이에 할당 i되고에 대해 확인 0됩니다. 0이 아닌 경우 while (true)루프는 내부 b()에서 계속되고 다시에 도달 suspendCoroutine하여 resume()호출이 반환되고 이제에서 또 다른 루프 단계를 거 칩니다 forEach(). 마지막으로로 다시 시작 0하면 println명령문이 실행되고 프로그램이 완료 될 때까지 계속 됩니다.

위의 분석은 "코 루틴을 일시 중단"한다는 것은 컨트롤을 가장 안쪽 launch호출 (또는 더 일반적으로 코 루틴 빌더 ) 으로 되 돌리는 것을 의미한다는 중요한 직관을 제공해야합니다 . 코 루틴이 재개 된 후 다시 일시 중지되면 resume()호출이 종료되고 제어가의 호출자에게 돌아갑니다 resume().

코 루틴 디스패처의 존재는 대부분이 즉시 다른 스레드에 코드를 제출하기 때문에이 추론을 덜 명확하게 만듭니다. 이 경우 위의 스토리는 다른 스레드에서 발생하고 코 루틴 디스패처도 continuation객체를 관리 하므로 반환 값을 사용할 수있을 때 다시 시작할 수 있습니다.


19

우선,이 IMO를 이해하는 가장 좋은 출처는 Roman Elizarov의 "Deep Dive into Coroutines" 강연 입니다.

코 루틴 또는 함수가 일시 중단됩니까?

일시 중단 호출 보내고 기능은 정지 들에게 또 다른 코 루틴을 실행하기 시작, 현재의 thread를 의미하는 코 루틴을. 따라서 코 루틴 은 함수가 아니라 일시 중단되었다고합니다.

실제로 이러한 이유로 기능을 중지하는 호출 사이트를 "중단 지점"이라고합니다.

어떤 코 루틴이 일시 중단됩니까?

코드를 살펴보고 어떤 일이 발생하는지 분석해 보겠습니다.

// 1. this call starts a new coroutine (let's call it C1).
//    If there were code after it, it would be executed concurrently with
//    the body of this async
async {
    ...
    // 2. this is a regular function call
    val deferred = computation()
    // 4. because await() is suspendING, it suspends coroutine C1.
    //    This means that if we had a single thread in our dispatcher, 
    //    it would now be free to go execute C2
    // 7. once C2 completes, C1 is resumed with the result `true` of C2's async
    val result = deferred.await() 
    ...
    // 8. C1 can now keep going in the current thread until it gets 
    //    suspended again (or not)
}

fun computation(): Deferred<Boolean> {
    // 3. this async call starts a second coroutine (C2). Depending on the 
    //    dispatcher you're using, you may have one or more threads.
    // 3.a. If you have multiple threads, the block of this async could be
    //      executed in parallel of C1 in another thread. The control flow 
    //      of the current thread returns to the caller of computation().
    // 3.b. If you have only one thread, the block is sort of "queued" but 
    //      not executed right away, and the control flow returns to the 
    //      caller of computation(). (unless a special dispatcher or 
    //      coroutine start argument is used, but let's keep it simple).
    //    In both cases, we say that this block executes "concurrently"
    //    with C1.
    return async {
        // 5. this may now be executed
        true
        // 6. C2 is now completed, so the thread can go back to executing 
        //    another coroutine (e.g. C1 here)
    }
}

아우터 async는 코 루틴을 시작합니다. 를 호출 computation()하면 내부 async가 두 번째 코 루틴을 시작합니다. 그 후, 호출 await()일시 중단을 실행하는 외부 async 의 실행까지 코 루틴, 내부 async 의 코 루틴은 끝난다.

단일 스레드로도 볼 수 있습니다. 스레드는 외부 async의 시작을 실행 한 다음 computation()내부 를 호출 하고 도달합니다 async. 이 시점에서 내부 비동기의 본문은 건너 뛰고 스레드는에 async도달 할 때까지 외부를 계속 실행합니다 await(). 정지 기능 await()이기 때문에 "일시 정지 지점" await입니다. 이것은 외부 코 루틴이 일시 중단되어 스레드가 내부 코 루틴을 실행하기 시작 함을 의미합니다. 완료되면 다시 돌아와 외부 async.

suspend는 내부 계산 코 루틴이 완료 될 때까지 외부 비동기 코 루틴이 대기 (대기)하는 동안 (외부 비동기 코 루틴) 유휴 상태 (따라서 이름 suspend)가 스레드 풀에 스레드를 반환하고 자식 계산 코 루틴이 완료 될 때를 의미합니까? , 그것은 (외부 비동기 코 루틴) 깨어나고 풀에서 다른 스레드를 취하고 계속됩니까?

네, 정확합니다.

이것이 실제로 달성되는 방법은 모든 일시 중지 기능을 상태 머신으로 전환하는 것입니다. 여기서 각 "상태"는이 일시 중지 기능 내부의 일시 중지 지점에 해당합니다. 내부적으로이 함수는 실행을 시작해야하는 서스펜션 지점에 대한 정보와 함께 여러 번 호출 될 수 있습니다.


3
훌륭한 대답입니다. 코 루틴에 관해서는 정말 기본적인 설명이 그립습니다.
bernardo.g

다른 언어로 구현되지 않는 이유는 무엇입니까? 아니면 내가 뭔가를 놓치고 있습니까? 저는 그 솔루션에 대해 오랫동안 생각하고 있습니다. Kotlin이 가지고있어서 기쁩니다.하지만 TS 나 Rust가 왜 그런지 잘 모르겠습니다
PEZO

@PEZO 잘 코 루틴은 오랫동안 사용되었습니다. Kotlin은 그것들을 발명하지 않았지만 구문과 라이브러리가 그것들을 빛나게합니다. Go에는 고 루틴, JavaScript 및 TypeScript에는 약속이 있습니다. 유일한 차이점은 사용하는 구문의 세부 사항입니다. JS의 async기능이 이런 식으로 표시되지만 여전히 Promise를 반환하는 것이 상당히 짜증나거나 방해가된다는 것을 알았습니다 .
Joffrey

죄송합니다. 제 의견이 명확하지 않습니다. 나는 suspend 키워드를 언급하고 있습니다. 비동기와 동일하지 않습니다.
PEZO

Roman의 비디오를 알려 주셔서 감사합니다. 순금.
Denounce'IN

8

이해하는 가장 좋은 방법 suspendthis키워드와 coroutineContext속성을 비유하는 것 입니다.

Kotlin 함수는 로컬 또는 전역으로 선언 할 수 있습니다. 로컬 함수는 마법처럼 this키워드에 액세스 할 수 있지만 전역 함수는 그렇지 않습니다.

Kotlin 함수는 suspend또는 차단 으로 선언 할 수 있습니다 . suspend함수는 마술처럼 coroutineContext속성에 액세스 할 수 있지만 차단 함수는 그렇지 않습니다.

문제는 coroutineContext속성 Kotlin stdlib에서 "일반"속성처럼 선언되지만이 선언은 문서 / 탐색 목적을위한 스텁 일뿐입니다. 사실 coroutineContext되어 고유 속성을 내장 하는 언어 키워드를 알고 그와 같은이 속성을 알고 후드 컴파일러 마법에서 의미합니다.

어떤 this키워드 지방 기능 일은 무엇 coroutineContext속성의 경우와 suspend기능 : 그것은 실행의 현재 컨텍스트에 액세스 할 수 있습니다.

따라서 현재 실행중인 코 루틴 컨텍스트의 인스턴스 인 속성에 suspend대한 액세스 권한을 얻어야합니다.coroutineContext


5

계속의 개념에 대한 간단한 예를 들었습니다. 이것은 일시 중지 기능이하는 일이며, 일시 중지 / 일시 중지 한 다음 계속 / 재개 할 수 있습니다. 쓰레드와 세마포의 관점에서 코 루틴을 생각하지 마세요. 연속 및 콜백 후크 측면에서 생각하십시오.

명확하게 말하면 코 루틴은 suspend함수 를 사용하여 일시 중지 할 수 있습니다 . 이것을 조사 해보자 :

Android에서는 예를 들어 다음과 같이 할 수 있습니다.

var TAG = "myTAG:"
        fun myMethod() { // function A in image
            viewModelScope.launch(Dispatchers.Default) {
                for (i in 10..15) {
                    if (i == 10) { //on first iteration, we will completely FREEZE this coroutine (just for loop here gets 'suspended`)
                        println("$TAG im a tired coroutine - let someone else print the numbers async. i'll suspend until your done")
                        freezePleaseIAmDoingHeavyWork()
                    } else
                        println("$TAG $i")
                    }
            }

            //this area is not suspended, you can continue doing work
        }


        suspend fun freezePleaseIAmDoingHeavyWork() { // function B in image
            withContext(Dispatchers.Default) {
                async {
                    //pretend this is a big network call
                    for (i in 1..10) {
                        println("$TAG $i")
                        delay(1_000)//delay pauses coroutine, NOT the thread. use  Thread.sleep if you want to pause a thread. 
                    }
                    println("$TAG phwww finished printing those numbers async now im tired, thank you for freezing, you may resume")
                }
            }
        }

위의 코드는 다음을 인쇄합니다.

I: myTAG: my coroutine is frozen but i can carry on to do other things

I: myTAG: im a tired coroutine - let someone else print the numbers async. i'll suspend until your done

I: myTAG: 1
I: myTAG: 2
I: myTAG: 3
I: myTAG: 4
I: myTAG: 5
I: myTAG: 6
I: myTAG: 7
I: myTAG: 8
I: myTAG: 9
I: myTAG: 10

I: myTAG: phwww finished printing those numbers async now im tired, thank you for freezing, you may resume

I: myTAG: 11
I: myTAG: 12
I: myTAG: 13
I: myTAG: 14
I: myTAG: 15

다음과 같이 작동한다고 상상해보십시오.

여기에 이미지 설명 입력

따라서 시작한 현재 함수는 중지되지 않고 계속되는 동안 코 루틴 만 일시 중지됩니다. 스레드는 일시 중지 기능을 실행하여 일시 중지되지 않습니다.

나는 이 사이트가 당신의 일을 바로 잡는 데 도움이 될 수 있고 나의 참고 자료 라고 생각한다 .

멋진 일을하고 반복 중간에 일시 중지 기능을 동결 해 보겠습니다. 나중에 다시 시작하겠습니다.onResume

라는 변수를 저장 continuation하면 코 루틴 연속 객체와 함께로드합니다.

var continuation: CancellableContinuation<String>? = null

suspend fun freezeHere() = suspendCancellableCoroutine<String> {
            continuation = it
        }

 fun unFreeze() {
            continuation?.resume("im resuming") {}
        }

이제 일시 중단 된 함수로 돌아가서 반복 중간에 멈 춥니 다.

 suspend fun freezePleaseIAmDoingHeavyWork() {
        withContext(Dispatchers.Default) {
            async {
                //pretend this is a big network call
                for (i in 1..10) {
                    println("$TAG $i")
                    delay(1_000)
                    if(i == 3)
                        freezeHere() //dead pause, do not go any further
                }
            }
        }
    }

그런 다음 onResume과 같은 다른 곳 (예 :) :

override fun onResume() {
        super.onResume()
        unFreeze()
    }

그리고 루프가 계속됩니다. 어느 시점에서든 일시 중단 기능을 동결하고 일정 시간이 지나면 재개 할 수 있다는 것을 아는 것은 매우 깔끔합니다. 채널을 살펴볼 수도 있습니다.


4

이미 많은 좋은 답변이 있으므로 다른 사람들을 위해 더 간단한 예제를 게시하고 싶습니다.

runBlocking 사용 사례 :

  • myMethod ()는 suspend함수입니다.
  • runBlocking { }차단 방식으로 코 루틴을 시작합니다. Thread클래스로 일반 스레드를 차단하고 특정 이벤트가 발생한 후 차단 된 스레드에 알리는 것과 비슷 합니다.
  • runBlocking { }않는 블록 코 루틴까지 스레드를 실행하는 전류를 (본체 사이 {}) 도착 완료

     override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
        runBlocking {
            Log.d(TAG,"Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
            myMethod();
        }
        Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
    }
    
    private suspend fun myMethod() {
        withContext(Dispatchers.Default) {
        for(i in 1..5) {
            Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
        }
    }
    

이것은 다음을 출력합니다.

I/TAG: Outer code started on Thread : main
D/TAG: Inner code started  on Thread : main making outer code suspend
// ---- main thread blocked here, it will wait until coroutine gets completed ----
D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-2
D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-2
// ---- main thread resumes as coroutine is completed ----
I/TAG: Outer code resumed on Thread : main

사용 사례 시작 :

  • launch { } 코 루틴을 동시에 시작합니다.
  • 즉, launch를 지정하면 코 루틴이 worker스레드에서 실행을 시작 합니다.
  • worker스레드 및 외부 스레드 (있는 우리가 전화 launch { }) 모두 동시에 실행됩니다. 내부적으로 JVM은 선점 형 스레딩을 수행 할 수 있습니다.
  • 여러 작업을 병렬로 실행해야하는 경우이를 사용할 수 있습니다. scopes코 루틴의 수명을 지정하는 것이 있습니다 . 를 지정 GlobalScope하면 코 루틴은 애플리케이션 수명이 끝날 때까지 작동합니다.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
    
        GlobalScope.launch(Dispatchers.Default) {
            Log.d(TAG,"Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
            myMethod();
        }
        Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
    }
    
    private suspend fun myMethod() {
        withContext(Dispatchers.Default) {
            for(i in 1..5) {
                Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
            }
        }
    }

이 출력 :

10806-10806/com.example.viewmodelapp I/TAG: Outer code started on Thread : main
10806-10806/com.example.viewmodelapp I/TAG: Outer code resumed on Thread : main
// ---- In this example, main had only 2 lines to execute. So, worker thread logs start only after main thread logs complete
// ---- In some cases, where main has more work to do, the worker thread logs get overlap with main thread logs
10806-10858/com.example.viewmodelapp D/TAG: Inner code started  on Thread : DefaultDispatcher-worker-1 making outer code suspend
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-1
10806-10858/com.example.viewmodelapp D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-1

asyncawait 사용 사례 :

  • 우리가 할 수있는 여러 작업이있을 때 그리고 그들은 타인의 완료에 따라, async그리고 await도움이 될 것이다.
  • 예를 들어, 아래 코드에는 2myMethod () 및 myMethod2 () 일시 중지 함수 가 있습니다 . myMethod2()의 전체 완료 후에 만 실행을하셔야합니다 myMethod() 또는이 myMethod2() 결과에 따라 myMethod(), 우리는 사용할 수 있습니다 asyncawait
  • async와 유사한 코 루틴을 병렬로 시작합니다 launch. 그러나 다른 코 루틴을 병렬로 시작하기 전에 하나의 코 루틴을 기다리는 방법을 제공합니다.
  • 그 방법은 await()입니다. async의 인스턴스를 반환합니다 Deffered<T>. TUnit기본값으로. 우리가 어떤 기다릴 필요시 async의 완성, 우리는 전화로 필요 .await()Deffered<T>그 예 async. 아래 예제에서와 같이 호출 이 완료 innerAsync.await()될 때까지 실행이 중단된다는 것을 의미합니다 innerAsync. 출력에서 동일한 것을 관찰 할 수 있습니다. 는 innerAsync호출하는 먼저 완료됩니다 myMethod(). 그리고 다음 async innerAsync2시작,myMethod2()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main_activity)
        Log.i(TAG,"Outer code started on Thread : " + Thread.currentThread().name);
    
         job = GlobalScope.launch(Dispatchers.Default) {
             innerAsync = async {
                 Log.d(TAG, "Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
                 myMethod();
             }
             innerAsync.await()
    
             innerAsync2 = async {
                 Log.w(TAG, "Inner code started  on Thread : " + Thread.currentThread().name + " making outer code suspend");
                 myMethod2();
             }
        }
    
        Log.i(TAG,"Outer code resumed on Thread : " + Thread.currentThread().name);
        }
    
    private suspend fun myMethod() {
        withContext(Dispatchers.Default) {
            for(i in 1..5) {
                Log.d(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
            }
        }
    }
    
    private suspend fun myMethod2() {
        withContext(Dispatchers.Default) {
            for(i in 1..10) {
                Log.w(TAG,"Inner code i : $i on Thread : " + Thread.currentThread().name);
            }
        }
    }

이것은 다음을 출력합니다.

11814-11814/? I/TAG: Outer code started on Thread : main
11814-11814/? I/TAG: Outer code resumed on Thread : main
11814-11845/? D/TAG: Inner code started  on Thread : DefaultDispatcher-worker-2 making outer code suspend
11814-11845/? D/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-2
11814-11845/? D/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-2
// ---- Due to await() call, innerAsync2 will start only after innerAsync gets completed
11814-11848/? W/TAG: Inner code started  on Thread : DefaultDispatcher-worker-4 making outer code suspend
11814-11848/? W/TAG: Inner code i : 1 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 2 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 3 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 4 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 5 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 6 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 7 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 8 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 9 on Thread : DefaultDispatcher-worker-4
11814-11848/? W/TAG: Inner code i : 10 on Thread : DefaultDispatcher-worker-4
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.