일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- coroutine
- 릴리즈 키해시
- cancellationException
- Android
- collectLatest
- SDUI
- TOSS 과제
- monotone stack
- 안드로이드
- flowon
- conflate
- Algorithm
- coldStream
- Product Flavor
- withContext
- coroutinescope
- Kotlin
- Flow
- 백준
- KAKAO
- Next Challenge
- hotStream
- coroutinecontext
- ServerDrivenUI
- app-distribution
- google play console
- 백준2309
- java
- ShapeableImageView
- Advanced LCA
- Today
- Total
루피도 코딩한다
[Coroutine Basics 2] Android에서의 Thread와 Coroutine 본문
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값으로 관리된다.
위 개념들이 안드로이드에서는 어떻게 적용되는가?
일반적으로 하나의 앱 = 하나의 Process
- (참고) 게임과 같이 다른 프로세스에서 자원을 필요로 하는 경우, 실행중이던 프로세스가 종료될수도 있다
- 하나의 프로세스가 기본인 이유는 보안 및 안정성상의 이유로 어플리케이션 간의 격리를 하기 위함이다.
- 그러나 개발자의 재량에 따라 하나의 앱에서 Multi Process를 실행 할 수도 있다.
하나의 앱에서 멀티 프로세스 사용하는 법
안드로이드의 4대 컴포넌트(Activity, Service, Broadcast Receiver, Contents Provider )가 선언되는 `AndroidManifest.xml` 에서 process를 지정할 수 있다 (안드로이드 공식 문서)
<application ...> <service android:name=".YourService" android:process=":your_process_name" /> ✅ ... </application>
권장되지 않는 이유
- 프로세스 간 통신을 하기 위해서는 IPC(Inter-Process Communication)가 필요하기에 비용이 많이 든다.
- 메모리 사용량 증가
- 따라서, Android의 시스템 리소스 관리는 단일 프로세스 애플리케이션에서 효율적으로 작동하도록 설계되어 있음
그럼에도 불구하고 멀티프로세스를 적용하는 경우
- 성능을 위한 병렬 실행이 필요할 때
- 지속적인 상태를 유지해야하는 백그라운드 서비스를 구현해야할 때
- 구성요소(ex. 서비스)간 안드로이드의 IPC 메커니즘을 사용해 통신해야하는 경우
Android에서의 스레드
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)이 발생
- 메인스레드가 Android UI toolkit의
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' 카테고리의 다른 글
[Coroutine Basics 6] Structured Concurrency, CoroutineScope, CoroutineContext (0) | 2024.01.22 |
---|---|
[Coroutine Basics 5] 코루틴의 예외 처리와 Job의 상태 변화 (1) | 2024.01.21 |
[Coroutine Basics 4] Coroutine Cacelation (0) | 2024.01.19 |
[Coroutine Basics 3] Coroutine Builder and Job (0) | 2024.01.18 |
[Coroutine Basics 1] Routine과 Coroutine의 개념과 예시 (0) | 2024.01.11 |