<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Reference</title>
    <link>https://hanseom.tistory.com/</link>
    <description>Developer를 위한 Reference 블로그 입니다.</description>
    <language>ko</language>
    <pubDate>Thu, 7 May 2026 18:21:18 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>hanseom</managingEditor>
    <image>
      <title>Reference</title>
      <url>https://tistory1.daumcdn.net/tistory/3355593/attach/974f6e8c68bc420d867b36eba6a68e61</url>
      <link>https://hanseom.tistory.com</link>
    </image>
    <item>
      <title>CoroutineContext</title>
      <link>https://hanseom.tistory.com/472</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CoroutineContext 구성요소&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CoroutineName&lt;/b&gt;&lt;/span&gt;: 코루틴의 이름 설정에 사용되는 객체입니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CoroutineDispatcher&lt;/b&gt;&lt;/span&gt;: 코루틴을 스레드에 보내서 실행하는 객체입니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Job&lt;/b&gt;&lt;/span&gt;: 코루틴을 조작하는데 사용되는 객체입니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CoroutineExceptionHandler&lt;/b&gt;&lt;/span&gt;: 코루틴에서 발생된 예외를 처리하는 객체입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CoroutineContext 구성요소를 관리하는 방법&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CoroutineContext 객체는 구성요소에 대한 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;키-값 쌍으로 구성요소를 관리&lt;/b&gt;&lt;/span&gt;합니다.&lt;/li&gt;
&lt;li&gt;각 구성요소는 고유한 키를 가지며, 키에 대해 중복된 값은 허용되지 않습니다.&lt;/li&gt;
&lt;li&gt;CoroutineContext 객체에 구성 요소를 추가하기 위해서는 더하기 연산자(+)를 사용하면 됩니다. CoroutineContext 객체는 키에 값을 직접 대입하는 방법을 사용하지 않습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749864432399&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val coroutineContext = newSingleThreadContext(&quot;MyThread&quot;) + CoroutineName(&quot;MyCoroutine&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;Key&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;Value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineName Key&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineName(&quot;MyCoroutine&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineDispatcher &lt;span style=&quot;background-color: #efefef; color: #333333; text-align: center;&quot;&gt;Key&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;newSingleThreadContext(&quot;MyThread&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;Job &lt;span style=&quot;background-color: #efefef; color: #333333; text-align: center;&quot;&gt;Key&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;설정되지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineExceptionHandler &lt;span style=&quot;background-color: #efefef; color: #333333; text-align: center;&quot;&gt;Key&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;설정되지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CoroutineContext 객체에 같은 구성요소가 둘 이상 더해지면, &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;나중에 추가된 구성요소가 이전 값을 덮어씌웁니다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;여러 구성요소로 이뤄진 CoroutineContext 두개가 합쳐질 때도 같은 키를 가진 구성요소가 있다면, 나중에 들어온 구성요소가 먼저 들어온 구성요소를 덮어 씌웁니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749864652094&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val coroutineContext = newSingleThreadContext(&quot;MyThread&quot;) + CoroutineName(&quot;MyCoroutine&quot;)
val newCoroutineContext = coroutineContext + CoroutineName(&quot;NewCoroutine&quot;)&lt;/code&gt;&lt;/pre&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-style=&quot;style12&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;Key&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;Value&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineName &lt;span style=&quot;background-color: #efefef; color: #333333; text-align: center;&quot;&gt;Key&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineName(&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;&quot;NewCoroutine&quot;&lt;/b&gt;&lt;/span&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineDispatcher &lt;span style=&quot;background-color: #efefef; color: #333333; text-align: center;&quot;&gt;Key&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;newSingleThreadContext(&quot;MyThread&quot;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;Job &lt;span style=&quot;background-color: #efefef; color: #333333; text-align: center;&quot;&gt;Key&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;설정되지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineExceptionHandler &lt;span style=&quot;background-color: #efefef; color: #333333; text-align: center;&quot;&gt;Key&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;설정되지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CoroutineContext 구성요소의 키&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CoroutineContext 구성요소에 접근하기 위해서는 구성요소가 가진 고유한 키가 필요합니다.&lt;/li&gt;
&lt;li&gt;CoroutineContext 구성요소의 키는 CoroutineContext.Key 인터페이스를 구현해 만들어집니다.&lt;/li&gt;
&lt;li&gt;CoroutineContext 구성요소는 보통 CoroutineContext.Key를 자신의 내부에 싱글톤 객체로 구현합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineContext 구성요소&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;Key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineName&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineName.Key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineDispatcher&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineDispatcher.Key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;Job&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;Job.Key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineExceptionHandler&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%; text-align: center;&quot;&gt;CoroutineExceptionHandler.Key&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CoroutineContext의 구성요소에 접근하기 위해서는 get 함수의 인자로 Key를 넘기면 됩니다.&lt;/b&gt;&lt;/span&gt; get 함수는 연산자 함수로 선언되어 있어서 대괄호 '[]'로 대체 가능합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749865347893&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
  val coroutineContext = CoroutineName(&quot;MyCoroutine&quot;) + Dispatchers.IO
  val nameFromContext = coroutineContext[CoroutineName.Key]
  println(nameFromContext)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; CoroutineName, Job, CoroutineDispatcher, CoroutineExceptionHandler는 동반 객체로 CoroutineContext.Key를 구현하는 Key를 가져서 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;'.Key'를 쓰지 않고도 구성요소에 접근 가능&lt;/b&gt;&lt;/span&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 단, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;CoroutineDispatcher을 키로 사용해 구성요소에 접근하면, 실행은 가능하지만 경고 메시지가 발생&lt;/b&gt;&lt;/span&gt;합니다. 이유는 CoroutineDispatcher.Key가 아직 Experimental API이기 때문입니다. 다음과 같이 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;OptIn 애노테이션&lt;/b&gt;&lt;/span&gt;을 붙이면 경고가 사라집니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749866717661&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

@OptIn(ExperimentalStdlibApi::class)
fun main() = runBlocking&amp;lt;Unit&amp;gt; {
  val coroutineContext = CoroutineName(&quot;MyCoroutine&quot;) + Dispatchers.IO
  val nameFromContext = coroutineContext[CoroutineDispatcher] // '.Key'제거
  println(nameFromContext)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;안정화된 API&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구성요소 인스턴스는 모두 key 프로퍼티를 가집니다. 이를 사용해 싱글톤 Key에 접근할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749866857801&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
  val coroutineName : CoroutineName = CoroutineName(&quot;MyCoroutine&quot;)
  val dispatcher : CoroutineDispatcher = Dispatchers.IO
  val coroutineContext = coroutineName + dispatcher

  println(coroutineContext[coroutineName.key])
  println(coroutineContext[dispatcher.key])
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;CoroutineContext 구성요소 제거하기&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CoroutineContext 객체에서 특정 구성요소를 제거하기 위해서는 minusKey 함수를 호출하고, 구성요소의 키를 넘기면 됩니다.&lt;/li&gt;
&lt;li&gt;minusKey 함수 사용 시 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;minusKey 함수를 호출한 CoroutineContext 객체는 그대로 유지되고 구성요소가 제거된 새로운 CoroutineContext 객체가 반환&lt;/b&gt;&lt;/span&gt;됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749867181619&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
  val coroutineName = CoroutineName(&quot;MyCoroutine&quot;)
  val dispatcher = Dispatchers.IO
  val myJob = Job()
  val coroutineContext: CoroutineContext = coroutineName + dispatcher + myJob

  val deletedCoroutineContext = coroutineContext.minusKey(CoroutineName)

  println(deletedCoroutineContext)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>BackEnd/coroutine</category>
      <category>CoroutineContext</category>
      <category>coroutinecontext 구성요소</category>
      <category>coroutinecontext 구성요소 제거하기</category>
      <category>coroutinecontext 구성요소를 관리하는 방법</category>
      <category>coroutinecontext 구성요소의 키</category>
      <category>coroutinedispatcher</category>
      <category>coroutineexceptionhandler</category>
      <category>CoroutineName</category>
      <category>Job</category>
      <category>optin 애노테이션</category>
      <author>hanseom</author>
      <guid isPermaLink="true">https://hanseom.tistory.com/472</guid>
      <comments>https://hanseom.tistory.com/472#entry472comment</comments>
      <pubDate>Sun, 15 Jun 2025 07:00:45 +0900</pubDate>
    </item>
    <item>
      <title>withContext</title>
      <link>https://hanseom.tistory.com/471</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;withContext 함수&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;withContext 함수는 인자로 받은 CoroutineDispatcher를 사용해 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;코루틴의 실행 스레드를 전환&lt;/b&gt;&lt;/span&gt;하고, 람다식의 코드를 실행한 후 결과값을 반환하는 함수입니다. 람다식을 실행한 후에는 스레드가 다시 이전의 Dispatcher을 사용하도록 전환됩니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;withContext를 사용하면 코루틴이 생성되지 않습니다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749863144573&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val result: String = withContext(Dispatchers.IO) {
        delay(1000L) // 네트워크 요청
        println(&quot;[${Thread.currentThread().name}] 결과값이 반환됩니다&quot;)
        return@withContext &quot;결과값&quot; // 결과값 반환
    }
    println(&quot;[${Thread.currentThread().name}] $result&quot;) // 결과값 출력
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;withContext vs async-await&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;async-await이 연속으로 호출되는 동작을 withContext로 대체 가능&lt;/b&gt;&lt;/span&gt;합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749863314601&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val result: String = async(Dispatchers.IO) {
        delay(1000L) // 네트워크 요청
        println(&quot;[${Thread.currentThread().name}] 결과값이 반환됩니다&quot;)
        &quot;결과값&quot; // 결과값 반환
    }.await()
    println(&quot;[${Thread.currentThread().name}] $result&quot;) // 결과값 출력
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;withContext&lt;span&gt;&amp;nbsp;주의점&lt;/span&gt;&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 다음과 같이 병렬 처리를 위해 withContext를 사용하면 안됩니다. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;withContext는 코루틴이 유지된 상태로 스레드만 바뀌기 때문에 순차 처리&lt;/b&gt;&lt;/span&gt;됩니다. 다음 코드의 경우, 약 2초가 걸립니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749863415440&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val result1: String = withContext(Dispatchers.IO) {
        delay(1000L) // 네트워크 요청
        &quot;결과값1&quot; // 결과값 반환
    }
    val result2: String = withContext(Dispatchers.IO) {
        delay(1000L) // 네트워크 요청
        &quot;결과값1&quot; // 결과값 반환
    }

    val results = listOf(result1, result2)
    println(&quot;[${Thread.currentThread().name}] ${results.joinToString(&quot;, &quot;)}&quot;) // 결과값 출력
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 병렬 처리를 위해서는 다음과 같이 async 함수를 사용해야 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749863598445&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val networkDeferred1: Deferred&amp;lt;String&amp;gt; = async(Dispatchers.IO) {
        delay(1000L) // 네트워크 요청
        &quot;결과값1&quot; // 결과값 반환
    }
    val networkDeferred2: Deferred&amp;lt;String&amp;gt; = async(Dispatchers.IO) {
        delay(1000L) // 네트워크 요청
        &quot;결과값2&quot; // 결과값 반환
    }

    val results: List&amp;lt;String&amp;gt; = awaitAll(networkDeferred1, networkDeferred2)
    println(&quot;[${Thread.currentThread().name}] ${results.joinToString(&quot;, &quot;)}&quot;) // 결과값 출력
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>BackEnd/coroutine</category>
      <category>async-await</category>
      <category>coroutine</category>
      <category>withcontext vs async-await</category>
      <category>withcontext 주의점</category>
      <category>withcontext 함수</category>
      <author>hanseom</author>
      <guid isPermaLink="true">https://hanseom.tistory.com/471</guid>
      <comments>https://hanseom.tistory.com/471#entry471comment</comments>
      <pubDate>Sat, 14 Jun 2025 11:00:00 +0900</pubDate>
    </item>
    <item>
      <title>async-await</title>
      <link>https://hanseom.tistory.com/470</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;async와 Deferred&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;async 코루틴 빌더를 호출하면 코루틴이 생성되고, Deferred&amp;lt;T&amp;gt; 타입 객체가 반환됩니다.&lt;/li&gt;
&lt;li&gt;Deferred는 Job과 같이 코루틴을 추상화한 객체이지만, 코루틴으로부터 생성된 결과값을 감싸는 기능을 추가로 가집니다.&lt;/li&gt;
&lt;li&gt;결과값의 타입은 제네릭 타입인 T로 표현됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749245649713&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public fun &amp;lt;T&amp;gt; CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -&amp;gt; T
): Deferred&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;async vs launch&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;async 함수도 launch 함수와 마찬가지로
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;context 인자로 CoroutineName이나 CoroutineDispatcher 설정이 가능합니다.&lt;/li&gt;
&lt;li&gt;start 인자로 CoroutineStart.LAZY를 설정해 지연 코루틴을 만들 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt; async 함수가 launch 함수와 다른 점은 block 람다식이 T를 반환한다는 점과 반환 객체가 Deferred라는 점&lt;/b&gt;&lt;/span&gt;입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749247739042&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public fun &amp;lt;T&amp;gt; CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -&amp;gt; Unit
): Job&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Deferred&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;Deferred는 Job의 서브타입으로,&lt;/span&gt; 코루틴으로부터의 결과값 수신을 위해 몇가지 함수만 추가된 인터페이스&lt;/b&gt;입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749248325971&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;public interface Deferred&amp;lt;out T&amp;gt; : Job&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Deferred 객체는 Job 객체의 모든 함수와 프로퍼티를 사용할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Deferred 객체의 제네릭 타입 지정&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Deferred 객체의 제네릭 타입을 지정하기 위해서는 Deferred에 명시적으로 타입을 설정하고, async 람다식에 반환값을 설정하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749247852377&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val networkDeferred = async(Dispatchers.IO) {
        delay(1000L) // 네트워크 요청
        return@async &quot;Dummy Response&quot; // Dummy Response 반환
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Deferred 객체의 동작 방식&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Deferred 객체는 미래의 어느 시점에 결과값이 반환될 수 있음을 표현하는 코루틴 객체&lt;/b&gt;&lt;/span&gt;입니다.&lt;/li&gt;
&lt;li&gt;코루틴이 실행 완료되었을 때 결과값이 반환되므로 언제 수신될 지 알 수 없습니다.&lt;/li&gt;
&lt;li&gt;결과값이 필요하다면 결과값이 수신될 때까지 대기해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Deferred 객체의 await 함수를 호출하면 Deferred 코루틴이 실행 완료될 때까지 await 함수를 호출한 코루틴이 일시 중단&lt;/b&gt;&lt;/span&gt;됩니다.&lt;/li&gt;
&lt;li&gt;Deferred 코루틴이 실행 완료되면 결과값이 반환되고 호출부의 코루틴이 재개됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749248137069&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val networkDeferred: Deferred&amp;lt;String&amp;gt; = async(Dispatchers.IO) {
        delay(1000L) // 네트워크 요청
        return@async &quot;Dummy Response&quot; // Dummy Response 반환
    }
    val result = networkDeferred.await() // 결과값이 반환될 때까지 runBlocking 일시 중단
    println(result) // 결과값 출력
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;복수의 코루틴으로부터 결과값 수신하기&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 다음 코드는 두 개의 코루틴을 실행하고 결과를 수신하는 예제입니다. 코드를 실행하여 출력 결과를 확인해보면 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;순차적으로 실행&lt;/b&gt;&lt;/span&gt;된 것을 확인할 수 있습니다. 이는 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;async-await이 연속적으로 호출됐기 때문&lt;/b&gt;&lt;/span&gt;입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1749248874062&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val startTime = System.currentTimeMillis() // 1. 시작 시간 기록

    // 2. 플랫폼1에서 등록한 관람객 목록을 가져오는 코루틴 실행
    val participantDeferred1: Deferred&amp;lt;Array&amp;lt;String&amp;gt;&amp;gt; = async(Dispatchers.IO) {
        delay(1000L)
        return@async arrayOf(&quot;철수&quot;,&quot;영수&quot;)
    }
    val participants1 = participantDeferred1.await() // 3. 결과가 수신 될 때까지 대기

    // 4. 플랫폼2에서 등록한 관람객 목록을 가져오는 코루틴 실행
    val participantDeferred2: Deferred&amp;lt;Array&amp;lt;String&amp;gt;&amp;gt; = async(Dispatchers.IO) {
        delay(1000L)
        return@async arrayOf(&quot;영희&quot;)
    }
    val participants2 = participantDeferred2.await() // 5. 결과가 수신 될 때까지 대기

    // 6. 지난 시간 표시 및 참여자 목록을 병합해 출력
    println(&quot;[${getElapsedTime(startTime)}] 참여자 목록: ${listOf(*participants1, *participants2)}&quot;)
}

fun getElapsedTime(startTime: Long): String = &quot;지난 시간: ${System.currentTimeMillis() - startTime}ms&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1749249241248&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 출력 결과
[지난 시간: 2024ms] 참여자 목록: [철수, 영수, 영희]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;코루틴을 병렬로 실행하기 위해서는 코루틴을 모두 실행한 다음 await을 호출해야 합니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1749249428576&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val startTime = System.currentTimeMillis() // 1. 시작 시간 기록

    // 2. 플랫폼1에서 등록한 관람객 목록을 가져오는 코루틴 실행
    val participantDeferred1: Deferred&amp;lt;Array&amp;lt;String&amp;gt;&amp;gt; = async(Dispatchers.IO) {
        delay(1000L)
        return@async arrayOf(&quot;철수&quot;,&quot;영수&quot;)
    }

    // 3. 플랫폼2에서 등록한 관람객 목록을 가져오는 코루틴 실행
    val participantDeferred2: Deferred&amp;lt;Array&amp;lt;String&amp;gt;&amp;gt; = async(Dispatchers.IO) {
        delay(1000L)
        return@async arrayOf(&quot;영희&quot;)
    }

    val participants1 = participantDeferred1.await() // 4. 결과가 수신 될 때까지 대기
    val participants2 = participantDeferred2.await() // 5. 결과가 수신 될 때까지 대기

    // 6. 지난 시간 표시 및 참여자 목록을 병합해 출력
    println(&quot;[${getElapsedTime(startTime)}] 참여자 목록: ${listOf(*participants1, *participants2)}&quot;)
}

private fun getElapsedTime(startTime: Long): String = &quot;지난 시간: ${System.currentTimeMillis() - startTime}ms&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1749249449714&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 출력 결과
[지난 시간: 1014ms] 참여자 목록: [철수, 영수, 영희]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;awaitAll 함수&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;awaitAll을 사용하면 복수의 Deferred 객체로부터 결과값을 수신&lt;/b&gt;&lt;/span&gt;할 수 있습니다.&lt;/li&gt;
&lt;li&gt;awaitAll 함수는 가변 인자로 Deferred 타입의 객체를 받아 인자로 받은 모든 Deferred로부터 결과가 수신될 때까지 호출부의 코루틴을 일시 중단합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1749249682473&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val startTime = System.currentTimeMillis() // 1. 시작 시간 기록

    // 2. 플랫폼1에서 등록한 관람객 목록을 가져오는 코루틴 실행
    val participantDeferred1: Deferred&amp;lt;Array&amp;lt;String&amp;gt;&amp;gt; = async(Dispatchers.IO) {
        delay(1000L)
        return@async arrayOf(&quot;철수&quot;, &quot;영수&quot;)
    }

    // 3. 플랫폼2에서 등록한 관람객 목록을 가져오는 코루틴 실행
    val participantDeferred2: Deferred&amp;lt;Array&amp;lt;String&amp;gt;&amp;gt; = async(Dispatchers.IO) {
        delay(1000L)
        return@async arrayOf(&quot;영희&quot;)
    }

    // 4. 두 개의 코루틴으로부터 결과가 수신될 때까지 대기
    val results = awaitAll(participantDeferred1, participantDeferred2)
    // val results = listOf(participantDeferred1, participantDeferred2).awaitAll() // Collection 확장함수

    // 5. 지난 시간 표시 및 참여자 목록을 병합해 출력
    println(&quot;[${getElapsedTime(startTime)}] 참여자 목록: ${listOf(*results[0], *results[1])}&quot;)
}

private fun getElapsedTime(startTime: Long): String = &quot;지난 시간: ${System.currentTimeMillis() - startTime}ms&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>BackEnd/coroutine</category>
      <category>Async</category>
      <category>async vs launch</category>
      <category>async-await</category>
      <category>await</category>
      <category>awaitall</category>
      <category>deferred</category>
      <category>deferred 객체의 동작 방식</category>
      <category>deferred 객체의 제네릭 타입 지정</category>
      <category>복수의 코루틴으로부터 결과값 수신하기</category>
      <author>hanseom</author>
      <guid isPermaLink="true">https://hanseom.tistory.com/470</guid>
      <comments>https://hanseom.tistory.com/470#entry470comment</comments>
      <pubDate>Sun, 8 Jun 2025 05:00:18 +0900</pubDate>
    </item>
    <item>
      <title>Coroutine 상태</title>
      <link>https://hanseom.tistory.com/469</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;Coroutine&amp;nbsp;상태&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sLD2f/btsOmtL9xc3/3VuEkyDyMNRwYCBCN9ml70/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sLD2f/btsOmtL9xc3/3VuEkyDyMNRwYCBCN9ml70/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sLD2f/btsOmtL9xc3/3VuEkyDyMNRwYCBCN9ml70/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsLD2f%2FbtsOmtL9xc3%2F3VuEkyDyMNRwYCBCN9ml70%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;604&quot; height=&quot;190&quot; data-origin-width=&quot;604&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 코루틴의 상태를 출력하기 위해서는 Job 객체를 프린트하면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;생성&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 생성 상태의 코루틴을 만들기 위해서는 지연 코루틴을 만들면 됩니다. 다음 코드는 코루틴이 실행 요청되기 전에 출력됐으므로, &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;생성(New) 상태&lt;/b&gt;&lt;/span&gt;를 출력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748656883259&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
  val job: Job = launch(start = CoroutineStart.LAZY) {
    delay(1000L)
  }
  println(job)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;실행 중&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 다음 코드는 코루틴을 실행하자마자 출력했으므로 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;실행중(Active) 상태&lt;/b&gt;&lt;/span&gt;를 출력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748656771317&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
  val job: Job = launch {
    delay(1000L)
  }
  println(job)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;실행 완료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 실행 완료 상태의 코루틴을 만들기 위해서는 join 함수를 호출하면 됩니다. 다음 코드는 코루틴 실행 완료 후 출력했으므로 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;실행 완료(Completed) 상태&lt;/b&gt;&lt;/span&gt;를 출력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748657015628&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
  val job: Job = launch {
    delay(1000L)
  }
  job.join() // launch 코루틴이 실행 완료될때까지 일시 중단
  println(job)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;취소 중&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 취소 중 상태를 만들기 위해서는 취소 확인 시점이 없는 코루틴을 만들고 취소하면 됩니다. 다음 코드는 취소 확인 시점이 없는 코루틴에 취소를 요청했으므로, &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;취소 중(Cancelling) 상태&lt;/b&gt;&lt;/span&gt;를 출력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748657126961&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
  val whileJob: Job = launch(Dispatchers.IO) {
    while(true){
      // 작업 실행
    }
  }
  whileJob.cancel() // 코루틴 취소 요청
  println(whileJob)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;취소 완료&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 취소 완료 상태를 만들기 위해서는 코루틴 내부에 일시 중단 시점을 만들고, cancelAndJoin 함수를 사용하면 됩니다. 다음 코드는 취소가 완료될 때까지 기다리기에 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;취소 완료(Cancelled) 상태&lt;/b&gt;&lt;/span&gt;를 출력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748657242357&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
  val whileJob: Job = launch(Dispatchers.IO) {
    while(true){
      yield() // 스레드 양보(일시 중단)
    }
  }
  whileJob.cancelAndJoin() // 코루틴 취소 완료될때까지 대기
  println(whileJob)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Job 객체의 상태 변수&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Job 객체는 코루틴을 추상화한 객체여서 코루틴의 상태를 간접적으로 나타내는 세가지 상태 변수를 외부로 공개합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;isActive&lt;/b&gt;&lt;/span&gt;: 코루틴이 활성화 되어 있는지 여부. 코루틴이 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;'실행 중' 상태일때 true&lt;/b&gt;&lt;/span&gt;입니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;isCancelled&lt;/b&gt;&lt;/span&gt;: 코루틴에 취소가 요청됐는지 여부. &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;cancel 함수가 호출되기만 하면 true를 반환하므로, 취소 중인 상태도 포함&lt;/b&gt;&lt;/span&gt;합니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;isCompleted&lt;/b&gt;&lt;/span&gt;: 코루틴이 완료 되었는지 여부. &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;코루틴이 실행 완료 되거나 취소 완료되면 true를 반환&lt;/b&gt;&lt;/span&gt;합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 126px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;&lt;b&gt;코루틴 상태&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;&lt;b&gt;isActive&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;&lt;b&gt;isCancelled&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;&lt;b&gt;isCompleted&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 21px;&quot;&gt;&lt;b&gt;생성&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;false&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;false&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 21px;&quot;&gt;&lt;b&gt;실행 중&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;true&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;false&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 21px;&quot;&gt;&lt;b&gt;실행 완료&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;false&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;false&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 21px;&quot;&gt;&lt;b&gt;취소 중&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;false&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;true&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 21px;&quot;&gt;
&lt;td style=&quot;width: 25%; height: 21px;&quot;&gt;&lt;b&gt;취소 완료&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;false&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;true&lt;/td&gt;
&lt;td style=&quot;width: 25%; height: 21px; text-align: center;&quot;&gt;true&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>BackEnd/coroutine</category>
      <category>coroutine 상태</category>
      <category>isActive</category>
      <category>iscancelled</category>
      <category>iscompleted</category>
      <category>job 객체의 상태 변수</category>
      <category>생성(new) 상태</category>
      <category>실행 완료(completed) 상태</category>
      <category>실행중(active) 상태</category>
      <category>취소 완료(cancelled) 상태</category>
      <category>취소 중(cancelling) 상태</category>
      <author>hanseom</author>
      <guid isPermaLink="true">https://hanseom.tistory.com/469</guid>
      <comments>https://hanseom.tistory.com/469#entry469comment</comments>
      <pubDate>Sat, 7 Jun 2025 23:00:56 +0900</pubDate>
    </item>
    <item>
      <title>Coroutine 취소</title>
      <link>https://hanseom.tistory.com/468</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;Coroutine 취소&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 코루틴 실행 도중, 더이상 코루틴을 실행할 필요가 없어지면 즉시 취소해야 합니다. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;취소하지 않으면 코루틴이 스레드를 계속해서 사용하기 때문에 애플리케이션의 성능 저하로 이어집니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;예) 사용자가 오래 걸리는 이미지 변환 작업을 요청한 후 취소한 경우&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 다음은 2500밀리초 이후 longJob 코루틴을 취소하는 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748588601960&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val startTime = System.currentTimeMillis()
    val longJob: Job = launch(Dispatchers.Default) {
        repeat(10) { repeatTime -&amp;gt;
            delay(1000L) // 1000밀리초 대기
            println(&quot;[${getElapsedTime(startTime)}] 반복횟수 ${repeatTime}&quot;)
        }
    }
    delay(2500L) // 2500밀리초 대기
    longJob.cancel() // 취소
}

fun getElapsedTime(startTime: Long): String =
    &quot;지난 시간: ${System.currentTimeMillis() - startTime}밀리초&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;cancel 함수&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;cancel 함수는 코루틴을 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;곧바로 취소하지 않습니다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;취소 확인용 플래그를 '취소 요청됨'으로 바꾸는 역할&lt;/b&gt;&lt;/span&gt;만 합니다.&lt;/li&gt;
&lt;li&gt;이후 취소 확인용 플래그가 확인되는 시점에 코루틴이 취소됩니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;cancel 함수를 사용하는 것은 순차성 관점에서 중요한 문제를 가집니다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 다음은 longJob 취소 요청 후 executeAfterJobCancelled 함수를 호출하는 예제 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748589157190&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val longJob: Job = launch(Dispatchers.Default) {
        Thread.sleep(1000L)
        println(&quot;longJob 코루틴의 동작&quot;)
    }
    longJob.cancel() // longJob 취소 요청
    executeAfterJobCancelled() // 취소 후 실행돼야 하는 동작
}

fun executeAfterJobCancelled() {
    println(&quot;longJob 코루틴 취소 후 실행돼야 하는 동작&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; cancel 함수를 호출하였음에도 longJob이 취소되지 않고, 다음과 같은 결과를 출력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748589277740&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[출력]
longJob 코루틴 취소 후 실행돼야 하는 동작
longJob 코루틴의 동작&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;코루틴 라이브러리는 취소의 순차성을 보장하기 위해 cancelAndJoin 함수를 제공&lt;/b&gt;&lt;/span&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;cancelAndJoin 함수&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 코루틴이 취소된 후 실행돼야 하는 코루틴이 있다면, 코루틴을 취소할 때 cancelAndJoin 함수를 사용하면 됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;cancelAndJoin = Cancel + Join&lt;/b&gt;&lt;/span&gt;: 취소 요청한 후 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;취소가 완료될 때까지 호출 코루틴 일시 중단&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1748589601222&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val longJob: Job = launch(Dispatchers.Default) {
        Thread.sleep(1000L)
        println(&quot;longJob 코루틴의 동작&quot;)
    }
    longJob.cancelAndJoin() // longJob 취소 요청 후 취소 완료될 때까지 호출 코루틴 일시 중단
    executeAfterJobCancelled() // 취소 후 실행돼야 하는 동작
}

fun executeAfterJobCancelled() {
    println(&quot;longJob 코루틴 취소 후 실행돼야 하는 동작&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1748589642239&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[출력]
longJob 코루틴의 동작
longJob 코루틴 취소 후 실행돼야 하는 동작&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;longJob 코루틴을 실행하자마자 취소하였음에도 취소되지 않는 이유는 코루틴의 취소 확인 시점이 없기 때문&lt;/b&gt;&lt;/span&gt;입니다. 코루틴은 기본적으로 일시중단 시점에 코루틴을 취소하는데, Thread.sleep 함수는 스레드를 블로킹 하기 때문입니다. 다음과 같이 Thread.sleep 함수를 delay 함수로 변경하고 실행하면 됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748589985159&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val longJob: Job = launch(Dispatchers.Default) {
        delay(1000L)
        println(&quot;longJob 코루틴의 동작&quot;)
    }
    longJob.cancelAndJoin() // longJob 취소 요청 후 취소 완료될 때까지 호출 코루틴 일시 중단
    executeAfterJobCancelled() // 취소 후 실행돼야 하는 동작
}

fun executeAfterJobCancelled() {
    println(&quot;longJob 코루틴 취소 후 실행돼야 하는 동작&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1748589999548&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[출력]
longJob 코루틴 취소 후 실행돼야 하는 동작&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Thread.sleep() vs delay()&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Thread.sleep()&lt;/b&gt;&lt;/span&gt;: 코루틴의 suspend 함수가 아니고, 단순히 현재 스레드를 블로킹합니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;delay()&lt;/b&gt;&lt;/span&gt;: &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;코루틴 suspend 함수&lt;/b&gt;&lt;/span&gt;로, 내부적으로 취소 신호를 체크합니다. 취소되면 즉시 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CancellationException&lt;/b&gt;&lt;/span&gt;을 던지고 코루틴을 중단합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Coroutine 취소 확인&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 코루틴이 취소를 확인하는 시점은 다음 두가지입니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;일시 중단 시점&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;코루틴이 실행을 대기하는 시점&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 다음 코드는 취소 확인 시점이 없어 &quot;작업 중&quot;을 계속 출력합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748655477606&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val whileJob: Job = launch(Dispatchers.Default) {
        while(true) {
            println(&quot;작업 중&quot;)
        }
    }
    delay(100L) // 100밀리초 대기
    whileJob.cancel() // 코루틴 취소
}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1748655596419&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[출력]
작업 중
작업 중
...
작업 중
작업 중
...
(무한 루프)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;코루틴에 취소 확인 시점을 만드는 세가지 방법&lt;/span&gt;&lt;/b&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;delay 함수&lt;/li&gt;
&lt;li&gt;yield 함수&lt;/li&gt;
&lt;li&gt;CoroutineScope.isActive&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;delay 함수&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; delay 함수는 특정 시간만큼 코루틴을 일시 중단하게 만듭니다. 단, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;delay 함수를 사용해 취소를 할 경우 불필요하게 작업을 지연시켜 성능 저하가 일어납니다.&lt;/b&gt;&lt;/span&gt; 다음 코드에서는 매 반복마다 1밀리초만큼 지연이 발생합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1748655731503&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val whileJob: Job = launch(Dispatchers.Default) {
        while(true) {
            println(&quot;작업 중&quot;)
            delay(1L)
        }
    }
    delay(100L)
    whileJob.cancel()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;yield 함수&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; yield 함수를 호출한 코루틴은 자신이 사용하던 스레드를 양보합니다. 양보한다는 것은 코루틴이 스레드 사용을 일시 중단 한다는 뜻입니다. &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;스레드를 양보한 후 곧바로 재개 요청됩니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748656119726&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val whileJob: Job = launch(Dispatchers.Default) {
        while(true) {
            println(&quot;작업 중&quot;)
            yield()
        }
    }
    delay(100L)
    whileJob.cancel()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;delay 함수와 yield 함수는 모두 일시중단 후 재개 과정을 거칩니다. 재개 시에는 CoroutineDispatcher에 의해 다시 스레드로 보내지는 과정을 거치기 때문에 비효율적입니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CoroutineScope.isActive&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; CoroutineScope의 isActive 프로퍼티를 사용하면 코루틴에 취소가 요청됐는지 확인할 수 있습니다. 코루틴에 취소가 요청되면, CoroutineScope.isActive가 false가 됩니다. &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CoroutineScope 객체의 isActive 확장 프로퍼티를 사용하면 코루틴을 일시 중단 시키지 않고 취소를 확인할 수 있습니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1748656363361&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val whileJob: Job = launch(Dispatchers.Default) {
        while(this.isActive) {
            println(&quot;작업 중&quot;)
        }
    }
    delay(100L)
    whileJob.cancel()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>BackEnd/coroutine</category>
      <category>cancel 함수</category>
      <category>cancelandjoin 함수</category>
      <category>cancellationexception</category>
      <category>coroutine 취소</category>
      <category>coroutine 취소 확인</category>
      <category>coroutinescope.isactive</category>
      <category>delay 함수</category>
      <category>Thread.sleep()</category>
      <category>yield 함수</category>
      <category>코루틴에 취소 확인 시점을 만드는 세가지 방법</category>
      <author>hanseom</author>
      <guid isPermaLink="true">https://hanseom.tistory.com/468</guid>
      <comments>https://hanseom.tistory.com/468#entry468comment</comments>
      <pubDate>Sun, 1 Jun 2025 05:00:11 +0900</pubDate>
    </item>
    <item>
      <title>지연 Coroutine</title>
      <link>https://hanseom.tistory.com/467</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;지연 Coroutine&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;지연 코루틴이란 &lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;즉시 실행 요청되지 않는 코루틴&lt;/span&gt;&lt;/b&gt;입니다.&lt;/li&gt;
&lt;li&gt;launch 함수의 start 인자로 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CoroutineStart.LAZY&lt;/b&gt;&lt;/span&gt;를 넘기면 지연 코루틴이 생성됩니다.&lt;/li&gt;
&lt;li&gt;지연 코루틴은 start 함수나 join 함수를 호출하면 실행됩니다.&lt;/li&gt;
&lt;li&gt;&lt;u&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;즉시 실행만 원하면 start 함수&lt;/b&gt;&lt;/span&gt;&lt;/u&gt;를, &lt;u&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;실행과 동시에 끝날 때까지 기다리려면 join 함수&lt;/b&gt;&lt;/span&gt;&lt;/u&gt;를 호출하면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1748587922520&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
  val startTime = System.currentTimeMillis()
  val lazyJob: Job = launch(start = CoroutineStart.LAZY) {
    println(&quot;[${getElapsedTime(startTime)}] launch 코루틴 지연 실행&quot;)
  }
  delay(3000L) // 3000밀리초간 대기
  lazyJob.start() // 코루틴 실행 lazyJob.join()을 호출해도 실행됨
}


fun getElapsedTime(startTime: Long): String =
  &quot;지난 시간: ${System.currentTimeMillis() - startTime}밀리초&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>BackEnd/coroutine</category>
      <category>coroutine</category>
      <category>CoroutineStart.LAZY</category>
      <category>join()</category>
      <category>start()</category>
      <category>즉시 실행 요청되지 않는 코루틴</category>
      <category>지연 coroutine</category>
      <author>hanseom</author>
      <guid isPermaLink="true">https://hanseom.tistory.com/467</guid>
      <comments>https://hanseom.tistory.com/467#entry467comment</comments>
      <pubDate>Sat, 31 May 2025 03:00:05 +0900</pubDate>
    </item>
    <item>
      <title>Coroutine 순차 처리</title>
      <link>https://hanseom.tistory.com/466</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; '토큰 업데이트 후 네트워크 요청', '이미지 변환 후 업로드 요청' 같이 작업 간에 선후 관계(종속성) 있는 작업들이 존재합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 다음은 순차처리 되지 않는 코드입니다. 토큰이 업데이트 되고 네트워크 요청을 해야하나, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;병렬로 처리되어 토큰 업데이트 완료 전 네트워크 요청이 일어납니다.&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1746770515596&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val updateTokenJob = launch(Dispatchers.IO) {
        println(&quot;[${Thread.currentThread().name}] 토큰 업데이트 시작&quot;)
        delay(100L) // 새로운 토큰을 가져오는데 걸리는 시간
        println(&quot;[${Thread.currentThread().name}] 토큰 업데이트 완료&quot;)
    }

    val networkCallJob = launch(Dispatchers.IO) {
        println(&quot;[${Thread.currentThread().name}] 네트워크 요청&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Job 객체&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;코루틴의 실행과 생명주기를 관리하는 핵심 인터페이스&lt;/b&gt;&lt;/span&gt;입니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;launch, async 같은 코루틴 빌더 함수는 코루틴을 생성할 때 Job 객체를 반환&lt;/b&gt;&lt;/span&gt;합니다.&lt;/li&gt;
&lt;li&gt;Job 객체를 통해&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;코루틴의 상태를 추적하고 제어&lt;/b&gt;&lt;/span&gt;할 수 있습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;join 함수&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Job 객체의 join 함수를 사용하면, &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;코루틴 순차 처리&lt;/b&gt;&lt;/span&gt;가 가능합니다. join 함수를 사용하면 join 함수의 대상이 된 코루틴이 완료될 때까지 join 함수를 호출한 코루틴이 일시 중단됩니다. 아래 코드에서는 runBlocking 코루틴이 updateTokenJob이 완료될 때까지 일시 중단됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746770708965&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val updateTokenJob = launch(Dispatchers.IO) {
        println(&quot;[${Thread.currentThread().name}] 토큰 업데이트 시작&quot;)
        delay(100L)
        println(&quot;[${Thread.currentThread().name}] 토큰 업데이트 완료&quot;)
    }
    updateTokenJob.join() // networkCallJob 실행 전 updateTokenJob.join() 호출

    val networkCallJob = launch(Dispatchers.IO) {
        println(&quot;[${Thread.currentThread().name}] 네트워크 요청&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;joinAll 함수&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; joinAll 함수를 사용하면 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;복수의 코루틴 순차 처리&lt;/b&gt;&lt;/span&gt;가 가능합니다. 실무에서는 서로 독립적인 복수의 코루틴을 병렬 실행한 후 이들이 모두 완료되었을 때 다음 작업을 실행해야 하는 경우가 많습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746771604434&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val convertImageJob1: Job = launch(Dispatchers.Default) {
        Thread.sleep(1000L) // 이미지1 변환 작업 실행
        println(&quot;[${Thread.currentThread().name}] 이미지1 변환 완료&quot;)
    }
    val convertImageJob2: Job = launch(Dispatchers.Default) {
        Thread.sleep(1000L) // 이미지2 변환 작업 실행
        println(&quot;[${Thread.currentThread().name}] 이미지2 변환 완료&quot;)
    }

    joinAll(convertImageJob1, convertImageJob2) // 이미지 1,2가 모두 변환될 때까지 대기

    listOf(convertImageJob1, convertImageJob2).joinAll()

    val uploadImageJob: Job = launch(Dispatchers.IO) {
        println(&quot;[${Thread.currentThread().name}] 이미지1,2 업로드&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;</description>
      <category>BackEnd/coroutine</category>
      <category>coroutine 순차 처리</category>
      <category>job 객체</category>
      <category>join 함수</category>
      <category>joinall 함수</category>
      <author>hanseom</author>
      <guid isPermaLink="true">https://hanseom.tistory.com/466</guid>
      <comments>https://hanseom.tistory.com/466#entry466comment</comments>
      <pubDate>Fri, 9 May 2025 23:50:48 +0900</pubDate>
    </item>
    <item>
      <title>CoroutineDispatcher</title>
      <link>https://hanseom.tistory.com/465</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CoroutineDispatcher&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코루틴을 스레드로 보내 실행시키는 객체&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; CoroutineDispatcher는 코틀린 코루틴의 핵심 구성요소 중 하나로, &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;코루틴이 어떤 스레드나 스레드 풀에서 실행될지를 결정하는 역할&lt;/b&gt;&lt;/span&gt;을 합니다. 즉, &quot;코루틴을 어디에서, 어떻게 실행할 것인가&quot;를 제어하는 실행 컨텍스트의 일부입니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;SingleThreadDispatcher&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; SingleThreadDispatcher는 오직 하나의 스레드에서만 코루틴을 실행하도록 보장하는 디스패처(Dispatcher)입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746483883467&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

val singleThreadDispatcher: CoroutineDispatcher =
    newSingleThreadContext(&quot;SingleThread&quot;)

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    launch(singleThreadDispatcher) {
        println(&quot;[${Thread.currentThread().name}] 실행&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;launch 함수의 인자로 singleThreadDispather를 넘기면 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;MultiThreadDispatcher&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; MultiThreadDispatcher는 여러 개의 스레드(스레드 풀)을 사용하는 디스패처(Dispatcher)입니다. 코루틴이 동시에 여러 스레드에서 병렬로 실행될 수 있도록 관리하는 역할을 합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746483981895&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

val multiThreadDispatcher: CoroutineDispatcher =
    newFixedThreadPoolContext(2, &quot;MultiThread&quot;) // 사용할 스레드 수, 스레드 풀에 속한 각 스레드의 이름 접두사

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    launch(multiThreadDispatcher) {
        println(&quot;[${Thread.currentThread().name}] 실행&quot;)
    }
    launch(multiThreadDispatcher) {
        println(&quot;[${Thread.currentThread().name}] 실행&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt; newFixedThreadPoolContext&amp;nbsp;함수의&amp;nbsp;문제&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;This is a delicate API and its use requires care.&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;text-align: center;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;i&gt;Make sure you fully read and understand documentation of the declaration that is marked as a delicate API. &lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; newFixedThreadPoolContext 함수는 고정된 개수의 스레드 풀을 생성하는 API입니다. 하지만 &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;리소스 누수 위험, 불필요한 스레드 생성, 비효율적인 컨텍스트 스위칭, 스레드 풀 분리로 인한 비효율 등 여러 가지 문제로 인해 API 자체가 &quot;delicate(섬세한)&quot;으로 분류&lt;/b&gt;&lt;/span&gt;되어, 문서를 충분히 읽고 이해한 후에만 사용해야 한다는 경고 문구가 발생합니다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;코루틴 라이브러리에서 제공하는 코루틴 디스패처&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Dispatchers.IO&lt;/li&gt;
&lt;li&gt;Dispatchers.Default&lt;/li&gt;
&lt;li&gt;Dispatchers.Main&lt;/li&gt;
&lt;li&gt;Dispatchers.Unconfined (무제한 디스패처의 경우, 이후 포스팅에서 다룹니다.)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Dispatchers.IO&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 네트워크 요청이나 DB 읽기 쓰기 같은 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;입출력(I/O) 작업을 실행하는 디스패처(Dispatcher)&lt;/b&gt;&lt;/span&gt;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;I/O 작업 전용&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;대규모 동시성 지원: 사용할 수 있는 스레드의 수(&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;64와 CPU 코어 수 중 큰 값&lt;/b&gt;&lt;/span&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746484647306&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    launch(Dispatchers.IO) { // 자식 코루틴에 디스패처가 설정되지 않으면, 부모 코루틴에 설정된 디스패처가 사용됩니다.
        launch {
            println(&quot;[${Thread.currentThread().name}] 작업1 실행&quot;)
        }
        launch {
            println(&quot;[${Thread.currentThread().name}] 작업2 실행&quot;)
        }
        launch {
            println(&quot;[${Thread.currentThread().name}] 작업3 실행&quot;)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt; Dispatchers.IO의&amp;nbsp;limitedParallelism &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 동시에 실행되는 IO 작업의 개수를 지정한 값만큼 제한하는 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;독립적인 디스패처 뷰&lt;/b&gt;&lt;/span&gt;를 만듭니다. 각 뷰는 서로 독립적으로 병렬성 제한을 가집니다. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;다른&amp;nbsp;작업에&amp;nbsp;방해&amp;nbsp;받지&amp;nbsp;않아야&amp;nbsp;하는&amp;nbsp;중요&amp;nbsp;작업이&amp;nbsp;있을&amp;nbsp;경우&amp;nbsp;사용&lt;/b&gt;&lt;/span&gt;합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746486040010&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;val dbDispatcher = Dispatchers.IO.limitedParallelism(10)
val fileDispatcher = Dispatchers.IO.limitedParallelism(5)

// DB 작업은 동시에 10개, 파일 작업은 동시에 5개까지만 실행
launch(dbDispatcher) { ... }
launch(fileDispatcher) { ... }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Dispatchers.Default&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 코틀린 코루틴에서 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;기본적으로 사용되는 디스패처(Dispatcher)&lt;/b&gt;&lt;/span&gt;입니다. 즉, 별도로 디스패처를 지정하지 않은 코루틴은 자동으로 Dispatchers.Default에서 실행됩니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;CPU 바운드 작업(이미지, 동영상 처리나 대용량 데이터 변환 같은 끊이지 않고 연산이 필요한 작업)에 최적화&lt;/span&gt;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;스레드 풀 크기: 내부적으로 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CPU 코어 수와 동일한 수(최소 2개)&lt;/b&gt;&lt;/span&gt;의 백그라운드 스레드 풀을 사용&lt;/li&gt;
&lt;li&gt;비블로킹 작업 권장: 블로킹 I/O(네트워크, 파일 등) 작업이 아닌, 연산 중심의 비블로킹 작업에 사용해야 효율적, 블로킹 작업은 Dispatchers.IO 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746485181683&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    launch(Dispatchers.Default) {
        launch {
            println(&quot;[${Thread.currentThread().name}] 작업1 실행&quot;)
        }
        launch {
            println(&quot;[${Thread.currentThread().name}] 작업2 실행&quot;)
        }
        launch {
            println(&quot;[${Thread.currentThread().name}] 작업3 실행&quot;)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt; Dispatchers.Default의 limitedParallelism &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;기존 디스패처의 병렬 실행 개수를 최대 n개로 제한하는 뷰를 생성&lt;/b&gt;&lt;/span&gt;합니다. 해당 뷰는 기존 디스패처의 스레드풀을 공유하지만, 동시에 실행되는 코루틴 수만 제한합니다. 즉, &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;스레드 수를 직접 제한하는 것이 아니라, 동시에 실행되는 코루틴 개수를 제한&lt;/b&gt;&lt;/span&gt;합니다. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;CPU 바운드 작업의 병렬성을 조절할 때 유용&lt;/b&gt;&lt;/span&gt;하며, 별도의 리소스 해제는 필요 없습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746486195141&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    val imageProcessingDispatcher = Dispatchers.Default.limitedParallelism(2)
    repeat(1000) {
        launch(imageProcessingDispatcher) {
            Thread.sleep(1000L) // 이미지 처리 작업
            println(&quot;[${Thread.currentThread().name}] 이미지 처리 완료&quot;)
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;코틀린 라이브러리의 공유 스레드풀&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Dispatchers.Default와 Dispatchers.IO는 JVM 프로세스 내에서 공유되는 스레드풀을 사용합니다. 즉, &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;같은 앱(프로세스) 내에서 여러 코루틴 디스패처가 하나의 스레드풀을 효율적으로 나누어 사용&lt;/b&gt;&lt;/span&gt;합니다. &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;공유 스레드풀에서는 스레드를 무제한으로 생성할 수 있고, 스레드는 필요에 따라 자동으로 늘어나거나 줄어드는 탄력적(elastic) 구조&lt;/b&gt;&lt;/span&gt;를 가지고 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Dispatchers.Main&lt;/b&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 메인 스레드에서의 작업을 위한 디스패처(Dispatcher)입니다. &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;주로 안드로이드, JavaFX, Swing 등 UI 프레임워크에서 UI 업데이트, 사용자 입력 처리 등 UI 관련 작업을 수행할 때 사용&lt;/b&gt;&lt;/span&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 기본 코루틴 라이브러리에는 구현체가 없습니다. &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;사용을 위해서는 안드로이드 코루틴 라이브러리(kotlinx-coroutines-android) 추가가 필요&lt;/b&gt;&lt;/span&gt;합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746486755979&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking(Dispatchers.Main) {
    launch {
        // 이 코드는 메인(UI) 스레드에서 실행됨
        println(&quot;Running on Main dispatcher&quot;)
        delay(1000)
        println(&quot;Task completed on Main thread&quot;)
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt; Dispatchers.Main.immediate &lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Dispatchers.Main은 어느 스레드에서 코루틴을 실행 요청하든 코루틴을 작업 대기열에 먼저 적재한 후 Main Thread가 비었을 때 코루틴을 보냅니다. &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Dispatchers.Main.immediate는 코루틴을 실행하는 코드가 메인 스레드에서 실행되고 있다면, 작업 대기열에 적재 없이 바로 메인 스레드에서 실행&lt;/b&gt;&lt;/span&gt;될 수 있게 합니다. 만약 백그라운드 스레드에서 코루틴을 실행 요청하면, 일반 Main과 동일하게 작업 대기열에 적재 후 메인 스레드로 보냅니다.&lt;/p&gt;</description>
      <category>BackEnd/coroutine</category>
      <category>coroutinedispatcher</category>
      <category>dispatchers.default</category>
      <category>dispatchers.io</category>
      <category>dispatchers.main</category>
      <category>dispatchers.main.immediate</category>
      <category>limitedparallelism</category>
      <category>multithreaddispatcher</category>
      <category>newfixedthreadpoolcontext 함수의 문제</category>
      <category>singlethreaddispatcher</category>
      <category>코루틴 라이브러리에서 제공하는 코루틴 디스패처</category>
      <author>hanseom</author>
      <guid isPermaLink="true">https://hanseom.tistory.com/465</guid>
      <comments>https://hanseom.tistory.com/465#entry465comment</comments>
      <pubDate>Tue, 6 May 2025 08:30:52 +0900</pubDate>
    </item>
    <item>
      <title>runBlocking</title>
      <link>https://hanseom.tistory.com/464</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;runBlocking&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; runBlocking 함수는 이 함수를 호출한 스레드를 사용해 실행되는 코루틴을 만들어냅니다. runBlocking 코루틴이 종료될 때 스레드 점유가 해제됩니다. 즉, &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;현재 스레드를 블로킹(중단)하여 코루틴 코드를 동기적으로 실행하는 함수&lt;/b&gt;&lt;/span&gt;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;runBlocking = Run(실행) + Blocking(차단)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;주요 특징&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;현재 스레드를 블로킹&lt;/span&gt;&lt;/b&gt;: runBlocking { ... } 블록 내 모든 작업이 끝날 때까지 해당 스레드를 멈춥니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;코루틴 월드로 진입&lt;/b&gt;&lt;/span&gt;: 일반 함수(main 등)에서는 launch, async 같은 코루틴 빌더를 바로 사용할 수 없습니다. &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;runBlocking을 사용하면 코루틴 스코프가 생성&lt;/b&gt;&lt;/span&gt;되어, 그 안에서 launch, async 등 코루틴 관련 함수를 사용할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;테스트나 진입점에서 주로 사용&lt;/b&gt;&lt;/span&gt;: main 함수, 단위 테스트 등 코루틴이 아닌 환경에서 코루틴을 실행할 때 주로 사용합니다. &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;앱의 UI 스레드 등에서 남용하면 성능 저하와 응답 불능이 발생할 수 있으니 주의해야 합니다.&lt;/b&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;CoroutineScope의 확장 함수가 아님&lt;/b&gt;&lt;/span&gt;: launch, async 등과 달리 runBlocking은 CoroutineScope의 확장 함수가 아니라, 스스로 코루틴 스코프를 만들어 실행합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 다음은 main 함수에서 runBlocking을 사용해 코루틴 스코프를 만들고, launch를 사용해 새로운 코루틴을 생성하는 코드입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746405682962&quot; class=&quot;kotlin&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;kotlin&quot;&gt;&lt;code&gt;import kotlinx.coroutines.*

fun main() = runBlocking&amp;lt;Unit&amp;gt; {
    println(&quot;[${Thread.currentThread().name}] runBlocking 코루틴 시작&quot;)
    launch {
        println(&quot;[${Thread.currentThread().name}] launch 코루틴 시작&quot;)
        delay(500L)
        println(&quot;[${Thread.currentThread().name}] launch 코루틴 종료&quot;)
    }
    delay(1000L)
    println(&quot;[${Thread.currentThread().name}] runBlocking 코루틴 종료&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;runBlocking은 main함수에서 코루틴을 동기적으로 실행&lt;/li&gt;
&lt;li&gt;launch로 시작된 코루틴은 runBlocking 내에서 비동기로 동작&lt;/li&gt;
&lt;li&gt;delay로 코루틴을 일시 중지할 수 있지만, 스레드는 블로킹되지 않음&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>BackEnd/coroutine</category>
      <category>coroutine</category>
      <category>runBlocking</category>
      <category>runblocking 특징</category>
      <author>hanseom</author>
      <guid isPermaLink="true">https://hanseom.tistory.com/464</guid>
      <comments>https://hanseom.tistory.com/464#entry464comment</comments>
      <pubDate>Mon, 5 May 2025 10:00:20 +0900</pubDate>
    </item>
    <item>
      <title>Coroutine 등장 배경</title>
      <link>https://hanseom.tistory.com/463</link>
      <description>&lt;h2 style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Overview&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 인프런-&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://www.inflearn.com/course/%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BD%94%EB%A3%A8%ED%8B%B4-%EC%99%84%EC%A0%84-%EC%A0%95%EB%B3%B5&quot;&gt;코틀린 코루틴 완전 정복&lt;/a&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;강의를 수강하고 정리합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a style=&quot;color: #0070d1;&quot; href=&quot;https://github.com/HanseomKim/coroutinelecture&quot;&gt;강의 소스 코드&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1746250969211&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;GitHub - HanseomKim/coroutinelecture: 『코틀린 코루틴 완전 정복』, 조세영, 인프런(2024) 저장소 입니다.&quot; data-og-description=&quot;『코틀린 코루틴 완전 정복』, 조세영, 인프런(2024) 저장소 입니다. Contribute to HanseomKim/coroutinelecture development by creating an account on GitHub.&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/HanseomKim/coroutinelecture&quot; data-og-url=&quot;https://github.com/HanseomKim/coroutinelecture&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/LsMou/hyYPpyLoV8/zbYCu4kMkGByX2EZejHJCk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/S15ot/hyYMgQ06P7/AduhfFsOGWOtJ5vxkwYaV0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/HanseomKim/coroutinelecture&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/HanseomKim/coroutinelecture&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/LsMou/hyYPpyLoV8/zbYCu4kMkGByX2EZejHJCk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/S15ot/hyYMgQ06P7/AduhfFsOGWOtJ5vxkwYaV0/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;GitHub - HanseomKim/coroutinelecture: 『코틀린 코루틴 완전 정복』, 조세영, 인프런(2024) 저장소 입니다.&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;『코틀린 코루틴 완전 정복』, 조세영, 인프런(2024) 저장소 입니다. Contribute to HanseomKim/coroutinelecture development by creating an account on GitHub.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;단일 스레드 애플리케이션&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 스레드 하나만 사용해 실행되는 애플리케이션입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;단일 스레드 애플리케이션의 한계&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 스레드는 한 번에 하나의 작업밖에 수행하지 못하기 때문에, 한 작업이 오래 걸리는 경우 문제가 됩니다. 메인 스레드 또한 예외가 아닙니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;멀티 스레드 프로그래밍&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 여러 개의 스레드를 사용해 작업을 처리하는 프로그래밍 기법입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Thread를 사용한 멀티 스레드 프로그래밍&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744442061878&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class ExampleThread : Thread() {
    override fun run() {
        println(&quot;[${Thread.currentThread().name}] 시작&quot;)
        Thread.sleep(2000L)
        println(&quot;[${Thread.currentThread().name}] 종료&quot;)
    }
}

fun main() {
    println(&quot;[${Thread.currentThread().name}] 시작&quot;)
    ExampleThread().start() // 새로운 Thread를 시작합니다.
    Thread.sleep(1000L)
    println(&quot;[${Thread.currentThread().name}] 종료&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Kotlin에서는 thread 함수를 제공하기 때문에 다음과 같이 작성할 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746252002241&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import kotlin.concurrent.thread

fun main() {
    println(&quot;[${Thread.currentThread().name}] 시작&quot;)
    thread {
        println(&quot;[${Thread.currentThread().name}] 시작&quot;)
        Thread.sleep(2000L)
        println(&quot;[${Thread.currentThread().name}] 종료&quot;)
    }
    Thread.sleep(1000L)
    println(&quot;[${Thread.currentThread().name}] 종료&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; Thread 클래스를 사용하면 간단하게 병렬 처리를 수행할 수 있다는 장점을 가지지만, 다음과 같은 한계가 존재합니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Thread의 start 함수를 호출할 때마다 새로운 스레드가 생성되고 재사용이 어렵습니다.&lt;/b&gt;&lt;/span&gt; 스레드는 비싼 자원이기에 재사용이 어려운 것은 치명적입니다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;개발자가 스레드 생성과 관리에 대한 책임을 가집니다.&lt;/b&gt;&lt;/span&gt; 즉, 개발자의 실수나 오류로 인해 메모리 누수가 일어날 수 있습니다. 또한, 프로그램이 복잡해질 수록 스레드의 생성과 관리를 직접 하는 것은 불가능에 가까워집니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Executor 프레임웍 &lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 위와 같은 문제를 해결하기 위해 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Java 1.5에서 Executor 프레임웍이 등장&lt;/b&gt;&lt;/span&gt;하였습니다. &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Executor 프레임웍이란 스레드의 집합인 스레드 풀을 미리 생성해놓고, 작업을 요청 받으면 쉬고 있는 스레드에 작업을 분배할 수 있는 시스템&lt;/b&gt;&lt;/span&gt;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;개발자가 더이상 스레드를 직접 관리하지 않도록 했습니다. 개발자는 스레드 개수 지정과 작업만 제출하면 됩니다.&lt;/li&gt;
&lt;li&gt;스레드의 재사용을 손쉽게 가능하게 만들었습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1746252801314&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.concurrent.Executors

fun main() {
    // ExecutorService 생성
    val executorService = Executors.newFixedThreadPool(2)

    // 작업1 제출
    executorService.submit {
        println(&quot;[${Thread.currentThread().name}] 작업1 시작&quot;)
        Thread.sleep(1000L) // 1초간 대기
        println(&quot;[${Thread.currentThread().name}] 작업1 완료&quot;)
    }

    // 작업2 제출
    executorService.submit {
        println(&quot;[${Thread.currentThread().name}] 작업2 시작&quot;)
        Thread.sleep(1000L) // 1초간 대기
        println(&quot;[${Thread.currentThread().name}] 작업2 완료&quot;)
    }

    // 작업3 제출
    executorService.submit {
        println(&quot;[${Thread.currentThread().name}] 작업3 시작&quot;)
        Thread.sleep(1000L) // 1초간 대기
        println(&quot;[${Thread.currentThread().name}] 작업3 완료&quot;)
    }

    // ExecutorService 종료
    executorService.shutdown()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;Executor 프레임웍의 한계&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;스레드 블로킹&lt;/b&gt;&lt;/span&gt;이 일어난다. 스레드 블로킹이란 스레드가 사용될 수 없는 상태에 있는 것을 뜻합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746252834073&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.concurrent.Executors
import java.util.concurrent.Future

fun main() {
    val executorService = Executors.newFixedThreadPool(2)

    // ExecutorService에 반환 값이 있는 작업 제출
    val future: Future&amp;lt;String&amp;gt; = executorService.submit&amp;lt;String&amp;gt; {
        Thread.sleep(2000)
        return@submit &quot;더미 결과값&quot;
    }

    // 반환값이 올때까지 메인 스레드 블로킹
    val result = future.get()
    println(result)

    executorService.shutdown()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt; CompletableFuture &lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Java 1.8부터는 CompletableFuture을 사용해 스레드 블로킹의 문제를 해결&lt;/b&gt;&lt;/span&gt;하였습니다. 다만, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;콜백 지옥이 생기고 예외 처리가 어렵다는 한계&lt;/b&gt;&lt;/span&gt;가 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1746252853426&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import java.util.concurrent.CompletableFuture
import java.util.concurrent.Executors

fun main() {
    val executorService = Executors.newFixedThreadPool(2)

    // ExecutorService에 반환 값이 있는 작업 제출
    val completableFuture = CompletableFuture.supplyAsync(
        {
            Thread.sleep(2000L)
            return@supplyAsync &quot;더미 결과값&quot;
        },
        executorService
    )

    // 콜백 형식으로 결과값 처리
    completableFuture.thenAccept { result -&amp;gt;
        println(&quot;결과:${result}&quot;)
    }

    executorService.shutdown()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;RxJava&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;결과값을 데이터 스트림으로 처리&lt;/b&gt;&lt;/span&gt;하였으나, &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;여전히 스레드 블로킹이 발생&lt;/b&gt;&lt;/span&gt;합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;스레드 기반 작업의 문제&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;스레드 기반 작업들은 작업의 전환이 어렵고, 전환에 드는 비용이 비쌉니다.&lt;/li&gt;
&lt;li&gt;간단한 작업에서는 앞서 본 콜백 방식을 사용하거나, 체이닝 함수를 사용하는 방식으로 스레드 블로킹 문제를 해결할 수 있지만, 실제 애플리케이션은 작업 간의 종속성이 복잡해 스레드 블로킹이 발생하는 것은 필연적입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;277&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/9Zkn8/btsNKxI5Mta/sCDsK8TaZ9wQBhEMZfZ5lK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/9Zkn8/btsNKxI5Mta/sCDsK8TaZ9wQBhEMZfZ5lK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/9Zkn8/btsNKxI5Mta/sCDsK8TaZ9wQBhEMZfZ5lK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F9Zkn8%2FbtsNKxI5Mta%2FsCDsK8TaZ9wQBhEMZfZ5lK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;784&quot; height=&quot;277&quot; data-origin-width=&quot;784&quot; data-origin-height=&quot;277&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;Coroutine&lt;/b&gt;&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; 코루틴에서는 '&lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;코루틴&lt;/b&gt;&lt;/span&gt;'이라 불리는 작업 단위를 사용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코루틴은 스레드의 사용 권한을 양보할 수 있습니다.&lt;/li&gt;
&lt;li&gt;즉, 스레드에 붙였다 뗐다 할 수 있는 작업 단위입니다.&lt;/li&gt;
&lt;li&gt;코루틴은 &lt;span style=&quot;color: #8a3db6;&quot;&gt;&lt;b&gt;경량 스레드&lt;/b&gt;&lt;/span&gt;입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock widthContent&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;294&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/becwPq/btsNKtUgSpe/c29NZOEOxuQDtHAQ7wCZVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/becwPq/btsNKtUgSpe/c29NZOEOxuQDtHAQ7wCZVk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/becwPq/btsNKtUgSpe/c29NZOEOxuQDtHAQ7wCZVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbecwPq%2FbtsNKtUgSpe%2Fc29NZOEOxuQDtHAQ7wCZVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;294&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;294&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>BackEnd/coroutine</category>
      <category>completablefuture</category>
      <category>coroutine</category>
      <category>executor</category>
      <category>executor 프레임웍</category>
      <category>rxjava</category>
      <category>thread</category>
      <category>단일 스레드 애플리케이션</category>
      <category>멀티 스레드 프로그래밍</category>
      <category>스레드 기반 작업의 문제</category>
      <category>스레드 블로킹</category>
      <author>hanseom</author>
      <guid isPermaLink="true">https://hanseom.tistory.com/463</guid>
      <comments>https://hanseom.tistory.com/463#entry463comment</comments>
      <pubDate>Sat, 3 May 2025 22:00:33 +0900</pubDate>
    </item>
  </channel>
</rss>