루피도 코딩한다

[Coroutine Basics 2] Android에서의 Thread와 Coroutine 본문

Coroutine

[Coroutine Basics 2] Android에서의 Thread와 Coroutine

xiaolin219 2024. 1. 17. 18:16

CS에서의 기본 개념

프로세스(Process)

  • 컴퓨터에서 실행되고 있는 프로그램
  • 각각 독립된 메모리 영역을 가짐

스레드(Thread)

  • 프로세스보다 작은 개념으로 프로세스에 소속되어 여러 코드를 동시에 실행할 수 있도록 해준다.
  • 스레드는 프로세스가 있어야만 존재할 수 있다
  • 동일한 프로세스 내부에 존재하기에 메모리 영역은 공유하며, 독립된 스택 영역을 가진다

코루틴(Coroutine)

  • 스레드보다 작은 개념
  • 코루틴은 스레드에서 실행됨

프로세스의 메모리 구조

  • Code : 실행할 프로그램의 코드가 저장되는 영역
  • Data : Static(정적) 변수나 Global(전역) 변수가 저장되는 영역
  • Heap : 동적 메모리 영역
  • Stack : 지역변수, 매개 변수, 반환 값 등 일시적은 데이터들이 저장되는 영역. LIFO(Last In First Out) 구조로 동작

여러개의 프로세스가 존재한다면, 각 프로세스마다 위와 같은 메모리 구조를 가진다.

Context Switching 관점에서의 프로세스, 스레드, 코루틴

  • 프로세스

    • 1번 프로세스에서 2번 프로세스로 실행이 변경되면 힙 영역과 스택 영역이 모두 교체되어야 한다
    • 프로세스간의 context switching 비용이 가장 큼 (오버헤드 문제가 존재함)
    • context switch시에는 PCB를 활용함
    • ex) 멀티 프로세스 → 웹 브라우저의 상단 탭 (동일한 프로그램 내부에서 여러개의 프로세스로 독립적으로 실행)
    • 하나의 프로레스에서 문제가 발생하더라도, 다른 프로세스 잘 동작 (웹 브라우저 첫번째 탭에서 문제가 발생해도 나머지 탭들은 정상 작동 함)
  • 스레드

    • 프로세스보다 context switching 비용이 적다

    • os가 스레드 실행에 관여하므로, os가 스레드를 강제로 멈추고 다른 스레드를 실행한다

    • 스레드가 병렬성으로 작동할지 동시성으로 작동할지는 CPU수에 의해 결정된다. 멀티 코어 환경에서는 병렬성(Parallelism)으로, 단일 코어 환경에서는 병렬성(Concurrency)으로 작동한다

  • 코루틴
    • 같은 코루틴이 다른 스레드에서 실행될수도, 다른 코루틴이 같은 스레드에서 실행될 수도 있다 (진입과 종료가 자유롭기때문)
    • 동일한 스레드에서 코루틴이 실행되면 메모리 전부를 공유하므로, 스레드보다 context switching 비용이 적다
    • 코루틴 스스로가 다른 코루틴에게 양보한다(yeild)
    • 동시성 확보

Thread in Java

  • Thread는 좌측의 이미지와 같이 java 내부에 runnable 인터페이스를 상속받은 class로 존재한다.
  • new, runnable, blocked와 같은 Thread의 상태는 Thread class 내부에 enum값으로 관리된다.

Untitled

Untitled

위 개념들이 안드로이드에서는 어떻게 적용되는가?

일반적으로 하나의 앱 = 하나의 Process

  • (참고) 게임과 같이 다른 프로세스에서 자원을 필요로 하는 경우, 실행중이던 프로세스가 종료될수도 있다
  • 하나의 프로세스가 기본인 이유는 보안 및 안정성상의 이유로 어플리케이션 간의 격리를 하기 위함이다.
  • 그러나 개발자의 재량에 따라 하나의 앱에서 Multi Process를 실행 할 수도 있다.
  1. 하나의 앱에서 멀티 프로세스 사용하는 법

    • 안드로이드의 4대 컴포넌트(Activity, Service, Broadcast Receiver, Contents Provider )가 선언되는 `AndroidManifest.xml` 에서 process를 지정할 수 있다 (안드로이드 공식 문서)

      <application ...>
        <service
            android:name=".YourService"
            android:process=":your_process_name" /> ✅
        ...
      </application>
  2. 권장되지 않는 이유

    • 프로세스 간 통신을 하기 위해서는 IPC(Inter-Process Communication)가 필요하기에 비용이 많이 든다.
    • 메모리 사용량 증가
    • 따라서, Android의 시스템 리소스 관리는 단일 프로세스 애플리케이션에서 효율적으로 작동하도록 설계되어 있음
  3. 그럼에도 불구하고 멀티프로세스를 적용하는 경우

    • 성능을 위한 병렬 실행이 필요할 때
    • 지속적인 상태를 유지해야하는 백그라운드 서비스를 구현해야할 때
    • 구성요소(ex. 서비스)간 안드로이드의 IPC 메커니즘을 사용해 통신해야하는 경우

Android에서의 스레드

  1. Main(UI) Thread

    • 메인스레드가 Android UI toolkit의 [android.widget](https://developer.android.com/reference/android/widget/package-summary)[android.view](https://developer.android.com/reference/android/view/package-summary) 패키지와의 통신을 대부분 담당한다.(아닐때도 있음) → 그래서 UI 스레드라고도 부르는것
    • 하나의 프로세스에서 실행되는 모든 컴포넌트는 UI 스레드에서 인스턴스화 되고, 시스템 콜도 여기서 전달됨.
    • 따라서 onKeyDown()과 같은 시스템 콜백, lifecycle 콜백에 응답하는 작업도 UI스레드에서 작동해야함
    • 약 5초 이상 UI 스레드가 block된다면 ANR(Application not responding)이 발생
  2. Worker(Sub) Thread

    • 실행하는데 오래걸려 UI 스레드를 오래 blocking 할 수 있는 job들은 worker thread를 활용해야한다.

    • 아래는 GPT가 알려준 예시 코드인데, Thread를 쓸때와 Coroutine의 차이를 볼 수 있다.

    • 기본적으로 Activity의 생명주기 함수는 Main Thread에서 작동하는게 기본이고, thread 블록을 열면 worker thread에서 작동하게 되는 구조이다.

    • 그러나 원칙적으로 UI는 Main Thread에서만 조작이 가능하므로 Handler객체를 만들어 UI Thread에 할 일을 전달하게 된다. 이를 통해 메인스레드는 차단되지 않을 수 있다.

      
        // Thread ver.
      
        class MainActivity : AppCompatActivity() {
      
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
      
                // Perform UI-related tasks on the main thread
                textView.text = "Hello from the main thread"
      
                // Offload a task to a worker thread
                thread {
                    // Simulate a time-consuming task
                    Thread.sleep(2000)
      
                    // Update UI on the main thread using Handler
                    Handler(Looper.getMainLooper()).post {
                        textView.text = "Task completed on a worker thread"
                    }
                }
            }
        }
      
        // Coroutine ver.
      
        class MainActivity : AppCompatActivity() {
      
            override fun onCreate(savedInstanceState: Bundle?) {
                super.onCreate(savedInstanceState)
                setContentView(R.layout.activity_main)
      
                // Perform UI-related tasks on the main thread
                textView.text = "Hello from the main thread"
      
                // Offload a task to a worker thread using Kotlin coroutines
                GlobalScope.launch(Dispatchers.Default) {
                    // Simulate a time-consuming task
                    Thread.sleep(2000)
      
                    // Update UI on the main thread using withContext
                    withContext(Dispatchers.Main) {
                        textView.text = "Task completed using Kotlin coroutines"
                    }
                }
            }
        }

cf. AsycTask

위 Thread 예시를 보면 thread내부에 Handler 코드를 작성해줌으로서 스레드별로 할 일을 나눠서 지정해주고 있다. 이 과정을 간소화 하기 위해 Android 에서는 AsyncTask라는 클래스를 제공해주고 있다. (여전히 이 방법보다는 코루틴이 짱이다)

  • 아래처럼 AsynckTask를 상속받고, doInBackground()와 onPostExecute() 함수를 오버라이드 해서 사용하게 된다. 위 Thread와 Handler 코드보다 직관적으로 볼 수 있는 장점이 있다.
class MyAsyncTask : AsyncTask<Void, Void, String>() {

    **override fun doInBackground(vararg params: Void?): String {**
        // 백그라운드에서 수행할 작업
        Thread.sleep(2000)
        return "Task completed"
    }

    **override fun onPostExecute(result: String) {**
        // UI 업데이트 작업
        textView.text = result
    }
}

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // AsyncTask 실행
        **MyAsyncTask().execute()**
    }
}

Reference

Processes and threads overview | App quality | Android Developers

Coroutine, Thread 와의 차이와 그 특징

inpa tistory

Comments