모닝스터디가 끝나고 면접 질문 스터디로 바뀐지 어연 3일째..
그간 잊고 살았던 기억들이 스몰스몰 올라오고 있는데, 이런 상황이 너무너무 답답해서 다시 한번 정리해보고자 한다.
프로세스는 프로그램(실행 파일)이 디스크에서부터 나와 메모리에서 적재되어 OS로부터 CPU 자원을 할당받아 실행중인 상태를 의미한다.
프로그램이 실행되어 프로세스가 되기까지의 과정은 다음과 같다.
1. 초기 프로그램은 디스크에 적재되어 있다.
2. 사용자가 프로그램을 실행하면 OS의 Loader가 프로그램의 코드를 읽은 뒤, RAM에서 비어있는 공간을 부여해준다.
이때 process라는 이름을 얻으며 동적인 상태가 된다.
3. process는 효율적인 관리를 위해 4가지 영역으로 나뉜다.(코드, 데이터, 힙, 스택)
4.OS는 process를 관리하기 위한 PCB를 만든다.(PID, 상태 정보 등이 들어있다)
5.Ready Queue에 대기하며, CPU를 점유 차례를 대기
6.OS가 차례를 할당하면 OS 디스패처(Dispatcher)가 process 정보를 CPU로 보낸다.

프로세스 내 논리적 작업 단위
Unix의 task 순차적 처리 특징에 따라 각 프로세스끼리 메모리를 공유하지 않아 프로세스 통신을 위해서는 추가적인 과정을 거침
이는 하나의 프로세스 내부에서 데이터를 공유해 동시에 여러가지 일을 처리하기 위한 Thread가 등장함
위 3가지 상황에 따라 멀티쓰레드의 이점이 달라질 수 있음
결론부터 말하자면, 가성비와 효율성때문이다.
쓰레드 사용은 싼 작업
프로세스를 만드는 것은 상당히 비싼 작업이다.
OS를 통해 프로세스가 사용할 메모리 공간을 계속해서 비우고 채우는 작업을 반복해야 하기 때문이다.
하지만, 쓰레드는 같은 프로세스 내에서 본인만의 Stack 영역을 같기 때문에 메모리 소모가 훨씬 적고 생성 및 종료 속도가 빠르다.
컨텍스트 스위칭 비용 감소
CPU가 A를 실행하다가 B를 실행하는 것을 컨텍스트 스위칭(Context Swithcing)이라고 한다.
프로세스 컨텍스트 스위칭은 CPU에 올라가 있는 캐시 메모리를 싹 비우고, 새로운 프로세스 정보를 올려야 하기에 상당히 무겁다.
반면, 쓰레드를 교체하게 되면 공유하는 자원을 제외하고 Stack 영역과 레지스터 정보만 갈아끼우면 돼서 훨씬 비용이 저렴하다.
가장 큰 이점이라고 생각했던 "메모리 공유"라는 부분에서 가장 큰 문제가 발생한다.
크게 4가지 문제점이 있다.
Race condition
두 개 이상의 쓰레드가 공유 자원에 동시적으로 접근하여 값을 사용할 때, 실행 순서에 따라 결과가 달라지는 현상이다.
DeadLock
DeadLock은 교착 상태를 의미한다.
두개 이상의 쓰레드가 서로가 가진 자원을 기다리며 무한정 대기에 빠지는 현상이다.
Starvation
Starvation은 기아 상태를 의미한다.
모든 쓰레드 역시 OS의 스케줄링에 따르는데, 특정 쓰레드의 우선순위가 낮아서 CPU 자원을 영원히 할당받지 못 할수도 있다.
Context Switching Overhead
스레드가 아주 많아지면 실제 작업 시간보다 Context Swtiching 시간이 더 길어질 수 있다.
일반적으로 C, C++, Java 등의 언어는 스레드가 메모리 공간을 공유하지만, Dart의 Isolate는 메모리를 전혀 공유하지 않는다.
오로지 자신만의 Heap 영역과 이벤트 루프를 가진다.
쓰레드는 메모리를 공유하기때문에 데이터를 쉽게 주고 받을 수 있었는데, Isolate는 그럼 데이터를 서로 주고 받을 수 없는 건가?
Isolate는 서로에게 메세지를 보낸다.
데이터를 복사해서 보내거나, 소유권을 직접 주는 형식이 있다.
데이터의 크기가 작을때는 복사해서 보내게 되는데, 소유권을 직접 주는 방식보다는 전송 속도가 느리다.
소유권을 넘겨줄 때는 원본 데이터를 직접 주기에 전송 속도가 빠르다.
물리적 관점으로 보면 Isolate 역시 OS 쓰레드를 사용한다.
하지만, 논리적으로 바라보면 OS 쓰레드 위에 격리된 장벽을 쳐놓은 추상화된 실행 단위이다.
Main 함수가 실행되는 곳을 Main Isolate라고 부르며, 무거운 작업이 실행될 때는 별도의 Worker Isolate를 생성 후 사용한다.
최종적으로 정리하자면, Dart는 싱글 쓰레드 이벤트 루프 방식을 사용한다.
CPU의 Ready Queue처럼 Queue 형식의 이벤트 루프를 사용해 작업들을 차례로 처리한다.
Isolate를 사용해 CPU 코어를 병렬로 사용할 수 있으며, 버벅거림(Jank) 등의 싱글쓰레드의 단점을 없앨 수 있다.