본문 바로가기
프로그래밍 놀이터/Kotlin, Coroutine

Coroutine 과 놀아보기 #1

by 돼지왕 왕돼지 2019. 5. 22.
반응형

Coroutine 과 놀아보기 #1


android studio, android studio coroutine, anr, Bridge, coroutine context, coroutine delay, coroutine scope, coroutine 과 놀기, Coroutine 과 놀아보기 #1, CoroutineContext, delay, delay non-blocking, handler, launch, main thread, mainthread, non blocking, Non-Blocking, onclick runblocking, runBlocking, runblocking dispatchers, single launch, suspend, thread share, what is runblocking, 기본 dispatcher



-

android studio 에서 coroutine 관련된 것들을 인식 못한다.

android 3.2.1 버전으로 업그레이드 하니 해결되었다.

해당 버전 이상으로 환경 세팅을 하고 시작하자!



-

이 글은 내가 coroutine 을 학습한 후에 약간 까리한 부분들을,

테스트 코드를 짜서 실험한 결과를 공유하는 글이다.





runBlocking?


새로운 coroutine 을 수행하며, 작업이 끝날 때까지 현재 thread 를 block 시킨다. (interrupt 가능하다.)



-

다음과 같은 테스트를 해보았다.

btn.setOnClickListener{
    runBlocking{
        startLogPrinting() // mainHandler 에서 50ms 마다 “logPrinting” 을 찍음

        logCurrentThread()
        log(“before delay”)
        delay(2000L)
        log(“after delay”)
    }
}

내가 예상한 결과는...

1. runBlocking 은 main thread 에서 불렸을 것이다.

2. delay 가 불렸을 때 main thread 가 block 되는 것이 아니라 “before delay” 를 찍기 전에  50ms 주기로 “log Printing” 들이 찍힐 것이라 생각


실제 결과는...

main

before delay

after delay

logPrinting

logPrinting

logPrinting


runBlocking 을 실행하는 순간, runBlocking coroutine 이 끝날때까지 Thread 를 계속 점유한다.

이 경우에는 MainThread 에서 runBlocking 을 수행했기 떄문에 Main 을 계속 점유하기 때문에, main handler 가 컨트롤을 가져가지 못한다.

이렇게 보면 결과가 이해가 된다.



-

여기서 한가지 궁금증이 생길 수 있다.

runBlocking(Dispatchers.Default){ } 로 로직을 수행시키면 결과가 달라질까?


답은, 결과는 같다!!

runBlocking 은 다시 말하지만 호출하는 thread 를 점유하고, runBlocking 의 coroutine 수행이 끝날때까지 wait 하는 로직이다.





기본 dispatcher?


-

main thread 에서 coroutineContext 지정 없이 launch 를 수행하면(runBlocking 안에서 launch) mainThread 에서 수행되고, 

mainThread 에서 GlobalScope.launch 를 coroutineContext 없이 전달하면,  DefaultDispathcer-worker-1 에서 수행된다.





runBlocking 안에서 MainThread 에 대한 launch?


-

btn.setOnClickListener{
    runBlocking{
        startLogPrinting() // mainHandler 에서 50ms 마다 “logPrinting” 을 찍음

        launch{
             logCurrentThread()
             log(“before delay”)
             delay(2000L)
             log(“after delay”)
        }
    }
}

runBlocking 이 Main Thread 를 점유하기 때문에 launch 코드를 못타지는 않을까 싶어 테스트해봤다.

launch 가 없는것과 동일한 결과를 낸다.





Multiple launch in runBlocking?


-

btn.setOnClickListener{
    runBlocking{
        launch{
            delay(10000L)
            log("10sec delay ends!")
       }
 
        launch{
             logCurrentThread()
             log(“before delay”)
             delay(2000L)
             log(“after delay”)
        }
 
        log(“How about this?”)
    }
}


앞서 single launch 를 돌리며 생각했다.

runBlocking 이 Main Thread 를 점유하는데 launch 를 탄 것은 동일 thread 이기 때문에 바로 실행한것인가?

아니면 coroutine scope 내에서의 main thread 점유는 우리가 알고 있는 coroutine 동작대로 동작할까?


이 실험의 결과는..

How about this?

main

before delay

after delay

10sec delay end

logPrinting

logPrinting

...


즉, runBlocking 을 호출하였더라도 coroutine context 내에서의 thread share? 는 동작한다.



-

자 그럼 이 결과는 어떻게 될까 예상해보라.

runBlocking{
    startLogPrinting() // mainHandler 에서 50ms 마다 “logPrinting” 을 찍음
    launch{
        logCurrentThread()
        log(“before delay 1”)
        delay(2000L)
        log(“after delay 1”)
    }

    launch{
        logCurrentThread()
        log(“before delay 2”)
        delay(1000L)
        log(“after delay 2”)
    }

    log("Yap!”)
}

결과는...



-

이번 놀아보기로 얻은 성과는...


1. runBlocking 이라는 것이 non suspend 환경과 suspend 환경의 bridge 가 된다는 것!

2. launch 를 main thread 에서 수행하더라도 block 단위로 queuing 되어 들어간다고 보면 된다는 것 (즉각 실행이 아님) - 사실 기본 중 기본!

3. coroutine 간에는 delay 가 non-blocking 으로 작동. (thread-share 됨)


괜히 handler 가 들어가면서 복잡해졌는데... 그렇지만 꼭 알아야 하는데...

1. coroutine 간에는 delay 가 non-blocking 으로 작동하지만, coroutine 이 아닌 것에는 blocking 처럼 작동한다..

2. 따라서 onClick runBlocking 을 수행하고 그 안에서 delay 를 주면… onClick 을 여러번 수행하면 ANR 에 걸릴 수 있다…. coroutine 바깥쪽 입장에서는 delay 가 Thread.sleep 과 비슷한 효과를 발휘한다.


마지막으로...

coroutine 의 실제 동작은 생각한 것과 참 많이 다를 수 있다.

직접 테스트해보고 쪼물쪼물 만져보며 익혀야 할 것 같다.




반응형

댓글