Coroutine 과 놀아보기 #2 |
-
Coroutine 과 놀아보기 #1 과 마찬가지로 공부를 하면서 의아스러운 부분들을 테스트 코드를 통해 검증하며 정리한 글입니다.
-
Job 이 cancel 되었을 때는 CancellationException 이 발생한다.
하지만, 이는 정상 동작으로 간주하여 coroutine 내에서는 crash 가 나진 않는다.
( runBlocking 안에서는 crash 가 난다.. runBlocking 은 따로 공부할 필요가 있다. )
-
withTimeout 의 경우는 일단 synchronous 한 동작을 한다. (withContext 와 비슷하다.)
아래와 같은 코드가 있다면 어떤 로그도 찍히지 않는다.
withTimeout(1000L){ delay(1500L) log("after delay") } log("afterTimeout")
-
withTimeout 의 경우는 시간 내에 coroutine 을 완료하지 않으면, TimeoutCancellationException 이 발생한다.
runBlocking 안에서 수행할 경우에는 exception 이 throw 되고, 그 외 coroutineScope 에서는 CancellationException 의 sub class 로서 exception 이 소비된다.
(runBlocking 에 대해서는 더 공부해보아야 할 필요가 있다)
-
async 는 launch 처럼 불리는 순간 로직이 수행된다.
await 나 start 등의 action 이 있을 때만 수행하게 하려면,, CoroutineStart.LAZY 값을 주면 된다.
-
ThreadLocal 쪽이 정확히 이해가 안 되어 이런 실험을 해보았다.
val threadLocal = ThreadLocal<String?>() runBlocking{ val job = launch{ logCurrentThread(“launch1”) log("threadLocal1 = ${threadLocal.get()}”) threadLocal.set(“1”) launch{ logCurrentThread(“launch2”) log("threadLocal2 = ${threadLocal.get()}”) threadLocal.set(“2”) launch{ logCurrentThread(“launch3”) log("threadLocal3 = ${threadLocal.get()}”) threadLocal.set(“3”) } } } job.join() log(“mainThread threadLocal = ${threadLocal.get()}”) }
결과는?
당연히 모두 main thread 이니 null, 1, 2, 3 이 순차적으로 찍힌다.
그렇다면? 모두 Default dispatcher 를 지정하면 어떻게 될까?
DefaultDispatcher-worker-1 by launch 1
threadLocal1 = null
DefaultDispatcher-worker-2 by launch 2
threadLocal2 = null
DefaultDispatcher-worker-2 by launch 3
threadLocal3 = 2
main threadLocal = null
원래 threadLocal 동작이 아는 데로 동작한다.
그렇다면... 왜 threadLocal 을 element 로 지정하는 것은 어떤 효과가 있어 소개가 된 것일까?
Dispatcher 에 + operator 로 threadLocal context (asContextElement) 를 더해본다.
그렇다면..
DefaultDispatcher-worker-1 by launch 1
threadLocal1 = null
DefaultDispatcher-worker-2 by launch 2
threadLocal2 = 1
DefaultDispatcher-worker-2 by launch 3
threadLocal3 = 2
main threadLocal = null
asContextElement 가 현재의 threadLocal 값을 기본으로 전달하기 때문에 전달받은 값을 사용하여 출력이 된다.
그리고 마지막 main thread 에서는 변경되지 않은 null 값을 참조한다.
결국 threadLocal 을 element 로 넘기면 thread 가 달라도 해당 thread 에 값을 넘긴다는 의미만 있는 것 같은데..
어떻게 써야 할지는 조금 더 고민해볼 필요가 있을 것 같다.
-
Channel 의 suspending 이 어떻게 되는지 궁금하여 다음과 같이 실험해보았다.
runBlocking{ val channel = Channel<Int>() launch{ for(x in 1..5){ val value = x * x log(“calculation $value”) channel.send(value) log(“channel sent $value”) } } repeat(5){ log(“repeat $i”) log(“channel received = “${channel.receive()}”) } }
결과는..
repeat 0
calculation 1
channel sent 1
calculation 4
channel received 1
repeat 1
channel received 4
repeat2
channel sent 4
calculation 9
channel sent 9
calculation 16
channel received 9
repeat 3
channel received 16
repeat 4
channel sent 16
calculation 25
channel sent 25
channel received 25
실제 순서의 보장이 어느 정도 의미있을지는 모르겠지만...
send 와 receive 사이의 suspending 의 resume timing 이.. 2개단위라는게 까리하다…
그래서 알아보니 Channel 의 capacity 기본값은 0 인 RENDEZVOUS 값이다.
이것이 영향이 있는것 같은데.. 이 녀석들의 정체를 정확히 모르겠다..
-
runBlocking 대신 GlobalScope.launch(Dispatchers.Main) 으로 하면…
MainThread 를 block 하지 않는다…
성과
1. ThreadLocal 은 어디에 쓸지 실무에서 써봐야 알겠다. asContextElement 로 전달하면 set 하는 값들이 적용되진 않는다.
2. Channel 의 send 와 receive 는 동작상의 문제는 없어보이지만, 정확히 어떤 mechanism 으로 resume 되는지 확실치 않다. capability 가 영향이 있을 것 같긴 한데..
3. runBlocking 대신 GlobalScope.launch(Dispatchers.Main) 을 사용하면.. Main thread 를 block 하지 않는다!!!
'프로그래밍 놀이터 > Kotlin, Coroutine' 카테고리의 다른 글
[coroutine] coroutineScope 의 동작 특성을 알아보자. (0) | 2020.03.08 |
---|---|
[coroutine] Coroutine Basics ( 코루틴 기초 ) (4) | 2020.03.07 |
Coroutine 과 놀아보기 #1 (0) | 2019.05.22 |
Coroutine first launch slow.. (0) | 2019.04.26 |
[coroutine] java.lang.IllegalStateException: Module with the Main dispatcher is missing. Add dependency providing the Main dispatcher, e.g. 'kotlinx-coroutines-android' (0) | 2019.03.06 |
댓글