본문 바로가기

컴퓨터 언어/Kotlin

06. 코루틴의 Job, waiting, Cancelation

Job은 코루틴의 상태를 가지고 있습니다.

코루틴을 실행하면 Job 객체를 반환합니다.

이 Job 객체를 가지고 코루틴 작업을 취소하거나 끝나는 것을 기다리는 등 여러 가지 작업을 할 수 있습니다.

 

[job의 cancel 메소드를 사용하여 코루틴을 정지해보기]

package com.goodee.test

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.databinding.DataBindingUtil
import com.goodee.test.databinding.ActivityMainBinding
import kotlinx.coroutines.*

class MainActivity : AppCompatActivity() {
    private val TAG: String = "로그"
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.nextBtn.setOnClickListener {
            Log.d(TAG,"MainActivity - Before :  run blocking ${Thread.currentThread().name}")

            // IO 스레드에서 코루틴을 시작하고, 코루틴이 반환하는 job을 받음.
            val job = CoroutineScope(Dispatchers.IO).launch {
                for (i in 1..70) {
                    Log.d(TAG,"MainActivity - [$i] returnHello : ${returnHello()}")
                }
            }

            // 메인 스레드를 정지하고 3초 후에 job을 캔슬.
            runBlocking {
                delay(3000)
                job.cancel()
                Log.d(TAG,"MainActivity - Canceled job")
            }
        }
    }

    private suspend fun returnHello(): String {
        delay(1000)
        return "Hello"
    }
}

[출력 결과]

2022-07-05 10:42:01.478 9222-9222/com.goodee.test D/로그: MainActivity - Before :  run blocking main
2022-07-05 10:42:02.487 9222-9253/com.goodee.test D/로그: MainActivity - [1] returnHello : Hello
2022-07-05 10:42:03.488 9222-9253/com.goodee.test D/로그: MainActivity - [2] returnHello : Hello
2022-07-05 10:42:04.488 9222-9222/com.goodee.test D/로그: MainActivity - Canceled job

cancel 하자마자 Hello 출력이 정지됨.

 


 

그러나, 실제로 코루틴을 정지 시킬 때는 위보다 복잡한 경우가 많습니다.

많은 연산이 필요한 코루틴을 실행하는 경우, 코루틴은 연산에 집중하느라 코루틴이 취소됐는지 확인할 시간이 없습니다.

[많은 연산을 실행했을 때 코루틴이 cancel에 어떻게 반응하냐?]

package com.goodee.test

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.databinding.DataBindingUtil
import com.goodee.test.databinding.ActivityMainBinding
import kotlinx.coroutines.*

class MainActivity : AppCompatActivity() {
    private val TAG: String = "로그"
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.nextBtn.setOnClickListener {
            Log.d(TAG,"MainActivity - Before :  run blocking ${Thread.currentThread().name}")

            // IO 스레드에서 코루틴을 시작하고, 코루틴이 반환하는 job을 받음.
            val job = CoroutineScope(Dispatchers.IO).launch {
                for (i in 30..45) {
                    Log.d(TAG,"MainActivity - [$i] fibonacci : ${fibonacci(i)}")
                }
            }

            // 메인 스레드를 정지하고 3초 후에 job을 캔슬.
            runBlocking {
                delay(3000)
                job.cancel()
                Log.d(TAG,"MainActivity - Canceled job")
            }
        }
    }

    // 시간복잡도가 O(2^n)인 피보나치 함수
    private fun fibonacci(n: Int): Long {
        return when {
            n == 0 -> 0
            n == 1 -> 1
            n > 1 -> fibonacci(n - 2) + fibonacci(n - 1)
            else -> -1
        }
    }
}

[출력 결과]

2022-07-05 10:53:12.005 9423-9423/com.goodee.test D/로그: MainActivity - Before :  run blocking main
2022-07-05 10:53:12.020 9423-9454/com.goodee.test D/로그: MainActivity - [30] fibonacci : 832040
2022-07-05 10:53:12.035 9423-9454/com.goodee.test D/로그: MainActivity - [31] fibonacci : 1346269
2022-07-05 10:53:12.057 9423-9454/com.goodee.test D/로그: MainActivity - [32] fibonacci : 2178309
2022-07-05 10:53:12.090 9423-9454/com.goodee.test D/로그: MainActivity - [33] fibonacci : 3524578
2022-07-05 10:53:12.140 9423-9454/com.goodee.test D/로그: MainActivity - [34] fibonacci : 5702887
2022-07-05 10:53:12.221 9423-9454/com.goodee.test D/로그: MainActivity - [35] fibonacci : 9227465
2022-07-05 10:53:12.355 9423-9454/com.goodee.test D/로그: MainActivity - [36] fibonacci : 14930352
2022-07-05 10:53:12.569 9423-9454/com.goodee.test D/로그: MainActivity - [37] fibonacci : 24157817
2022-07-05 10:53:12.926 9423-9454/com.goodee.test D/로그: MainActivity - [38] fibonacci : 39088169
2022-07-05 10:53:13.603 9423-9454/com.goodee.test D/로그: MainActivity - [39] fibonacci : 63245986
2022-07-05 10:53:14.511 9423-9454/com.goodee.test D/로그: MainActivity - [40] fibonacci : 102334155
2022-07-05 10:53:15.012 9423-9423/com.goodee.test D/로그: MainActivity - Canceled job
2022-07-05 10:53:15.013 9423-9423/com.goodee.test I/Choreographer: Skipped 179 frames!  The application may be doing too much work on its main thread.
2022-07-05 10:53:16.015 9423-9454/com.goodee.test D/로그: MainActivity - [41] fibonacci : 165580141
2022-07-05 10:53:18.395 9423-9454/com.goodee.test D/로그: MainActivity - [42] fibonacci : 267914296
2022-07-05 10:53:22.314 9423-9454/com.goodee.test D/로그: MainActivity - [43] fibonacci : 433494437
2022-07-05 10:53:28.711 9423-9454/com.goodee.test D/로그: MainActivity - [44] fibonacci : 701408733
2022-07-05 10:53:39.410 9423-9454/com.goodee.test D/로그: MainActivity - [45] fibonacci : 1134903170

 

cancel이 됐음에도 불구하고 모든 for문을 다 돌고 coroutine이 끝나는 것을 볼 수 있습니다.

그렇다면 코루틴을 바로 종료시키려면 어떻게 해야할까요?


첫번째 방법은 delay를 통해 연산을 멈추고 코루틴의 상태를 확인하는 틈을 주는 것입니다.

package com.goodee.test

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.databinding.DataBindingUtil
import com.goodee.test.databinding.ActivityMainBinding
import kotlinx.coroutines.*

class MainActivity : AppCompatActivity() {
    private val TAG: String = "로그"
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.nextBtn.setOnClickListener {
            Log.d(TAG,"MainActivity - Before :  run blocking ${Thread.currentThread().name}")

            // IO 스레드에서 코루틴을 시작하고, 코루틴이 반환하는 job을 받음.
            val job = CoroutineScope(Dispatchers.IO).launch {
                for (i in 30..45) {
                    delay(1000)
                    Log.d(TAG,"MainActivity - [$i] fibonacci : ${fibonacci(i)}")
                }
            }

            // 메인 스레드를 정지하고 3초 후에 job을 캔슬.
            runBlocking {
                delay(3000)
                job.cancel()
                Log.d(TAG,"MainActivity - Canceled job")
            }
        }
    }

    // 시간복잡도가 O(2^n)인 피보나치 함수
    private fun fibonacci(n: Int): Long {
        return when {
            n == 0 -> 0
            n == 1 -> 1
            n > 1 -> fibonacci(n - 2) + fibonacci(n - 1)
            else -> -1
        }
    }
}

[출력 결과]

2022-07-05 10:55:55.761 9575-9575/com.goodee.test D/로그: MainActivity - Before :  run blocking main
2022-07-05 10:55:56.804 9575-9607/com.goodee.test D/로그: MainActivity - [30] fibonacci : 832040
2022-07-05 10:55:57.824 9575-9607/com.goodee.test D/로그: MainActivity - [31] fibonacci : 1346269
2022-07-05 10:55:58.775 9575-9575/com.goodee.test D/로그: MainActivity - Canceled job
2022-07-05 10:55:58.777 9575-9575/com.goodee.test I/Choreographer: Skipped 180 frames!  The application may be doing too much work on its main thread.

 

job이 cancel 되자마자 작업이 종료되는 것을 볼 수 있음. 

 


두번째로는 연산할 때 코루틴의 상태를 확인하는 것입니다.

코루틴은 6가지 상태를 가지고 있습니다.

특정 작업을 시행하기 전에 코루틴의 상태를 체크하면, 코루틴이 취소 됐어도 작업을 계속하는 일은 일어나지 않습니다.

코루틴의 상태

package com.goodee.test

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.databinding.DataBindingUtil
import com.goodee.test.databinding.ActivityMainBinding
import kotlinx.coroutines.*

class MainActivity : AppCompatActivity() {
    private val TAG: String = "로그"
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.nextBtn.setOnClickListener {
            Log.d(TAG,"MainActivity - Before :  run blocking ${Thread.currentThread().name}")

            // IO 스레드에서 코루틴을 시작하고, 코루틴이 반환하는 job을 받음.
            val job = CoroutineScope(Dispatchers.IO).launch {
                for (i in 30..45) {
                    // 코루틴이 active 상태인지 확인
                    if (isActive) {
                        Log.d(TAG,"MainActivity - [$i] fibonacci : ${fibonacci(i)}")
                    }
                }
            }

            // 메인 스레드를 정지하고 3초 후에 job을 캔슬.
            runBlocking {
                delay(3000)
                job.cancel()
                Log.d(TAG,"MainActivity - Canceled job")
            }
        }
    }

    // 시간복잡도가 O(2^n)인 피보나치 함수
    private fun fibonacci(n: Int): Long {
        return when {
            n == 0 -> 0
            n == 1 -> 1
            n > 1 -> fibonacci(n - 2) + fibonacci(n - 1)
            else -> -1
        }
    }
}

[출력 결과]

2022-07-05 11:04:30.592 9733-9733/com.goodee.test D/로그: MainActivity - Before :  run blocking main
2022-07-05 11:04:30.607 9733-9764/com.goodee.test D/로그: MainActivity - [30] fibonacci : 832040
2022-07-05 11:04:30.622 9733-9764/com.goodee.test D/로그: MainActivity - [31] fibonacci : 1346269
2022-07-05 11:04:30.644 9733-9764/com.goodee.test D/로그: MainActivity - [32] fibonacci : 2178309
2022-07-05 11:04:30.677 9733-9764/com.goodee.test D/로그: MainActivity - [33] fibonacci : 3524578
2022-07-05 11:04:30.727 9733-9764/com.goodee.test D/로그: MainActivity - [34] fibonacci : 5702887
2022-07-05 11:04:30.810 9733-9764/com.goodee.test D/로그: MainActivity - [35] fibonacci : 9227465
2022-07-05 11:04:30.953 9733-9764/com.goodee.test D/로그: MainActivity - [36] fibonacci : 14930352
2022-07-05 11:04:31.164 9733-9764/com.goodee.test D/로그: MainActivity - [37] fibonacci : 24157817
2022-07-05 11:04:31.527 9733-9764/com.goodee.test D/로그: MainActivity - [38] fibonacci : 39088169
2022-07-05 11:04:32.083 9733-9764/com.goodee.test D/로그: MainActivity - [39] fibonacci : 63245986
2022-07-05 11:04:32.971 9733-9764/com.goodee.test D/로그: MainActivity - [40] fibonacci : 102334155
2022-07-05 11:04:33.600 9733-9733/com.goodee.test D/로그: MainActivity - Canceled job
2022-07-05 11:04:33.600 9733-9733/com.goodee.test I/Choreographer: Skipped 179 frames!  The application may be doing too much work on its main thread.
2022-07-05 11:04:34.491 9733-9764/com.goodee.test D/로그: MainActivity - [41] fibonacci : 165580141

 

cancel하자 작업이 취소되는 것을 볼 수 있습니다.

 


 

[출처 및 더 자세하고 정확한 설명은 밑에 블로그에서]

https://thdev.tech/kotlin/2019/04/08/Init-Coroutines-Job/

 

728x90
반응형

'컴퓨터 언어 > Kotlin' 카테고리의 다른 글

코틀린 - const val vs val  (0) 2022.09.30
07. 코루틴의 Async and Await  (0) 2022.07.06
05. 코루틴의 runBlocking  (0) 2022.07.05
04. 코루틴의 Context  (0) 2022.07.05
03. 코루틴 suspend 함수  (0) 2022.07.05