@Throws(InterruptedException::class)publicactualfun<T>runBlocking(context:CoroutineContext,block:suspendCoroutineScope.()->T):T{contract{callsInPlace(block,InvocationKind.EXACTLY_ONCE)}valcurrentThread=Thread.currentThread()valcontextInterceptor=context[ContinuationInterceptor]valeventLoop:EventLoop?valnewContext:CoroutineContextif(contextInterceptor==null){// create or use private event loop if no dispatcher is specified
eventLoop=ThreadLocalEventLoop.eventLoopnewContext=GlobalScope.newCoroutineContext(context+eventLoop)}else{// See if context's interceptor is an event loop that we shall use (to support TestContext)
// or take an existing thread-local event loop if present to avoid blocking it (but don't create one)
eventLoop=(contextInterceptoras?EventLoop)?.takeIf{it.shouldBeProcessedFromContext()}?:ThreadLocalEventLoop.currentOrNull()newContext=GlobalScope.newCoroutineContext(context)}valcoroutine=BlockingCoroutine<T>(newContext,currentThread,eventLoop)coroutine.start(CoroutineStart.DEFAULT,coroutine,block)returncoroutine.joinBlocking()}
coroutine의 일반적인 규칙은 thread를 block하지 않는다 이지만, runblocking은 다른 coroutine builder와는 다르게 쓰레드를 block함
main function과 같이, 쓰레드를 block하지마 않으면 프로세스가 종료되기 때문에 이러한 경우 runBlocking을 사용해야함
runBlocking은 새로운 코루틴을 실행하고, 현재 쓰레드를 코루틴이 완료될 때 까지 block함
runBlocking인자로 Dispatcher를 전달하여 다른 쓰레드에서 runBlocking을 실행하게 할 수 있음
다른 쓰레드에서 runBlocking을 실행해도 runBlocking을 실행한 쓰레드를 Block함
Dispatcher는 코루틴을 실행할 쓰레드를 선택하는 것으로, 현재 쓰레드를 block하는 것을 막을 수 없음
CoroutineScope의 확장함수가 아님, CoroutineScope외부에서 사용 가능
publicsuspendfun<T>withContext(context:CoroutineContext,block:suspendCoroutineScope.()->T):T{contract{callsInPlace(block,InvocationKind.EXACTLY_ONCE)}returnsuspendCoroutineUninterceptedOrReturnsc@{uCont->// compute new context
valoldContext=uCont.context// Copy CopyableThreadContextElement if necessary
valnewContext=oldContext.newCoroutineContext(context)// always check for cancellation of new context
newContext.ensureActive()// FAST PATH #1 -- new context is the same as the old one
if(newContext===oldContext){valcoroutine=ScopeCoroutine(newContext,uCont)return@sccoroutine.startUndispatchedOrReturn(coroutine,block)}// FAST PATH #2 -- the new dispatcher is the same as the old one (something else changed)
// `equals` is used by design (see equals implementation is wrapper context like ExecutorCoroutineDispatcher)
if(newContext[ContinuationInterceptor]==oldContext[ContinuationInterceptor]){valcoroutine=UndispatchedCoroutine(newContext,uCont)// There are changes in the context, so this thread needs to be updated
withCoroutineContext(coroutine.context,null){return@sccoroutine.startUndispatchedOrReturn(coroutine,block)}}// SLOW PATH -- use new dispatcher
valcoroutine=DispatchedCoroutine(newContext,uCont)block.startCoroutineCancellable(coroutine,coroutine)coroutine.getResult()}}
결과를 리턴한점에서 async와 많이 비교됨
withContext는 block이 끝날때까지 현재 coroutine을 suspend함
async와의 차이점
인자로 CoroutineStart을 받지않는 것
즉시 실행되므로 CoroutineStart가 필요없음
context의 디폴트 값이 없는 것
withContext는 현재 Context가 아닌 다른 Context로 실행할경우 사용하는것이기에, Context를 인자로 받아야함