0. 큰 그림
큰 그림의 개념 부분을 이번 포스팅으로 작성하겠습니다~
1. 코루틴, Coroutine
비선점형 멀티태스킹(non-preemptive multitasking) 을 수행하는 일반화한 서브루틴
즉, 서로 협력해서 실행을 주고받으면서 작동하는 여러 서브 루틴이다.
🙉 멀티태스킹/ 비선점형 멀티태스킹 / 선점형 멀티태스킹 이란?
먼저, 멀티태스킹 이란?
여러 작업을 동시에 수행하는 것처럼 보인다. or 실제로 동시에 수행한다.
비선점형 멀티태스킹
- CPU를 차지하고 있는 쓰레드가 CPU 연산이 필요 없음을 나타냈을 때만 운영체제가 이를 중단시킬 수 있다.
- 강제로 죽이려면, 리셋해야 한다.
- 각 참여자들이 서로 자발적으로 협력해야 한다.
선점형 멀티태스킹
- CPU를 차지하고 있는 쓰레드를 운영체제가 강제로 중단시킬 수 있다.
🙉 서브루틴 이란?
- 여러 명령어를 모아 이름을 부여해서 반복 호출할 수 있게 정의한 프로그램 구성요소
- Ex. 함수, 메소드
- 진입: only one
- 서브 루틴의 맨 처음부터 실행 → 활성 레코드(activation record) 가 스택에 할당 → 서브 루틴 내부의 로컬 변수 등의 초기화
- 실행 중단: return or 함수 끝 → 중단 지점이 여러 개 있을 수 있다!
- 제어를 호출한 쪽에게 돌려 준다. → 활성 레코드가 스택에서 사라진다.
- 서브루틴을 여러 번 반복 실행해도 항상 같은 결과를 반복해서 얻게 된다. (전역변수나 다른 부수 효과가 없을 경우)
- HOW?→ 서브 루틴이 호출될 때마다 저장된 메모리로 이동했다가 return 을 통해 원래 호출자의 위치로 돌아오게 된다.
- 별도의 메모리에 해당 기능을 모아 놓고 있어 → 서브 루틴이 호출될 때마다 저장된 메모리로 이동했다가 return 을 통해 원래 호출자의 위치로 돌아오게 된다.
2. 그래서... 코루틴은 Thread 랑 어떻게 다르다는 거지?
🙉 먼저, 프로세스 / 스레드 란?
- Process: 프로그램 이 메모리에 적재되어 실행되는 인스턴스
- Thread: Process 내 실행되는 여러 흐름 단위
프로그램이 실행되면, Process 가 생성되면서 Heap 영역과 하나의 Thread, 하나의 Stack 영역을 갖게 된다. → Thread 가 추가 될때마다 그 수만큼 Stack 이 추가된다.
결론부터 말하자면, Thread 와 코루틴은 작업의 단위가 다르다.
Thread는 작업의 단위가 Thread!
코루틴은 작업의 단위가 Thread 안의 작은 Thread 처럼 동작하는 코루틴(Object)!
즉, 코루틴은 Thread 안에서 실행되는 일시중단 가능한 작업의 단위
(= Thread 하나를 일시중단 가능한 다중 경량 Thread 처럼 사용하는 것)
🙉 흠.. Process 내 실행 흐름의 단위는 Thread 라고 배웠는데.. 코루틴은 어떻게 작업 단위로 가능한거지?
- Thread: 자신만이 사용할 수 있는 고유의 Stack 을 할당받는다.
- 코루틴: Process의 Heap 메모리를 공유하여 사용한다. (함수에 가까운 구조) - Stack 할당 X
더 자세한 내용은 여기를 참고해주세요! https://cliearl.github.io/posts/android/coroutine-principle/
3. 그럼 코루틴은 어떤 게 좋을까?
- 각 스레드마다 갖는 Stack 메모리 영역을 갖지 않기 때문에
→ 스레드 사용시 스레드 개수만큼 Stack 메모리에 따른 메모리 사용공간이 증가하지 않아도 된다.
- 코루틴은 Heap 메모리를 공유하여 사용하기 때문에
→ 같은 프로세스내에 ‘공유 데이터 구조’(Heap)에 대한 Locking 걱정이 없다.
- Thread 를 만드는 데 비용이 크다(Context Switching). 코루틴은 Thread 를 새로 생성하지 않기 때문에
→ 작업 전환 시의 오버헤드가 줄어든다.
자원 낭비 측면에서 얼마나 효율적인 지, 좀 더 직관적으로 이해하기 위해, 예시를 봐 볼까요~?
상황: 작업1 에서 작업 2의 결과가 필요한 경우
Thread
- Thread1 에서 작업1을 수행하고,
- Thread2 에서 작업2를 수행한다.
Thread1 은 Thread2의 작업이 끝날 때 까지 기다려야 한다.(Blocking)
즉, Thread1은 그동안 놀게!!! 된다. → 이것은, 바로 자.원.낭.비.
코루틴
- Thread1 에서 작업1과 작업3 수행, (Thread1 에서 작업 2개를 수행 할 수 있는 이유는? 코루틴이기 때문!)
- Thread2 에서 작업2를 수행한다.
Thread1 의 작업1 이 Thread2 의 작업2 결과를 기다리는 동안, Thread1 는 작업3 을 수행하면 된다!
즉, Thread1의 자원을 최대한 사용할 수 있다.
그렇다면, 이번에는 작업 전환 시 오버헤드가 얼마나 줄어드는 지,
Context Switching 횟수를 눈으로 비교 할 수 있는 예제를 봐볼까요~?
상황: 작업1 에서 작업 2의 결과가 필요하고 && 작업3, 작업4 진행도 필요한 경우
Thread
- Thread1(A) 에서 작업1 수행
- Thread2(B) 에서 작업2 수행
- Thread3(C) 에서 작업3 수행
- Thread4(D) 에서 작업4 수행
작업1 에서는 작업2의 결과값이 필요하므로, 두번째 그림에서 작업2가 수행되는 동안, Thread1(A) 는 Blocking 된다.
그 후, 작업2 의 결과값이 나오면, Thread1(A) 의 작업1, Thread3(C) 의 작업3, Thread4(D) 의 작업4 수행된다.
❔이 때, Context Switching 은 얼마나 일어날까? (싱글 코어 CPU 기준)
❕Thread1(A) 에서 작업1 수행 → Thread2(B) 에서 작업2 수행 → [ Thread1(A) 에서 작업1 수행 → Thread3(C) 에서 작업3 수행 → Thread4(D) 의 작업4 수행 ] * 반복
코루틴
- Thread1(A) 에서 작업1, 작업2 수행
- Thread3(C) 에서 작업3, 작업4 수행
작업1 에서는 작업2의 결과값이 필요하므로, 두번째 그림에서 작업2가 수행되는 동안, 작업1 은 Suspending 된다. 그러나, Thread1 자체는 Blocking 되지 않는다.
그 후, 작업2 의 결과값이 나오면, Thread1(A) 의 작업1, Thread3(C) 의 작업3, 작업4 가 수행된다.
❔이 때, Context Switching 은 얼마나 일어날까? (싱글 코어 CPU 기준)
❕[ Thread1(A) 에서 작업1 수행 → Thread3(C) 에서 작업3, 작업4 수행 ] * 반복
작업1과 작업2, 작업3과 작업4 의 Thread 가 같기 때문에 작업 전환 시에 Context Switching 이 필요 없다.
Thread 와 코루틴의 ContextSwitch 횟수 차이
그래서... 어쨌거나 결론은!
Thread는 Thread,
코루틴은 코루틴 !
나중에는 코루틴 보다 작은... 코루틴처럼 동작하는 게 나올...까?ㅎㅎ
.참고
https://cliearl.github.io/posts/android/coroutine-principle/
https://aaronryu.github.io/2019/05/27/coroutine-and-thread/
'Language > Kotlin' 카테고리의 다른 글
[Coroutine] Coroutine Scope: 코루틴이 실행되는 범위 (0) | 2022.02.05 |
---|---|
[Coroutine] 코루틴 사용하기 (0) | 2022.02.05 |
[KotlinInAction] 5장-람다로 프로그래밍(5) 수신 객체 지정 람다: with 와 apply (0) | 2021.12.08 |
[KotlinInAction] 5장-람다로 프로그래밍(5) 수신 객체 지정 람다: with 와 apply (0) | 2021.12.08 |
[KotlinInAction] 5장-람다로 프로그래밍(1) 람다 식과 멤버 참조 (0) | 2021.11.24 |