728x90
반응형
인프런 "모든 개발자의 실무를 위한 필수 기본기 클래스" 강의를 듣고 정리한 내용입니다.
프로세스와 스레드
프로세스
- 운영체제에 의해 연속적으로 실행되고 있는 프로그램
- 프로그램을 실행했다면 프로세스는 메모리 위에 올라가 있게 되며 운영체제에 의해 CPU, 메모리 자원을 할당받게 된다.
- 프로세스는 독립된 메모리 영역으로 Code, Data, Stack, Heap을 할당받는다.
- Code
- 개발자가 작성한 코드
- Data
- 전역변수, 정적변수, 배열, 구조체 등 프로그램이 실행되면서 생기는 정적인 데이터들이 저장된다.
- Stack
- 함수 호출과 관련된 정보(실행정보, 지역변수, 파라미터 등)이 저장된다. 일반적으로 컴파일 타임에 Stack의 크기가 결정된다.
- 📘 Stack Overflow는 프로세스에 할당된 Stack 메모리가 초과했을 때 생기는 문제다. 함수에 너무 큰 지역변수를 선언하거나 재귀적으로 무한정으로 함수를 호출하게 될 때 발생한다. → 컴파일 타임에서 스택의 크기가 결정되기 때문에 런타임에서 이런 현상이 발생하게 될 수 있다.
- Heap
- 런타임에 동적으로 메모리 처리해야 하는 상황에서 사용되는 공간
- Array를 사용하거나 외부 파일을 읽을 때 등을 예로 들 수 있다.
- 사용자에 의해 메모리 공간이 동적으로 할당되고 해제될 수 있다.
- 런타임에 동적으로 메모리 처리해야 하는 상황에서 사용되는 공간
- Code
스레드
- 특정한 시점에 프로그램의 작업을 수행하는 역할을 한다. 프로세스 안에서 실제 코드를 실행하는 단위라고 보면 된다.
- CPU 이용의 기본 단위. 한 개의 스레드 작업은 1개의 CPU 코어에 할당되게 된다.
- 스레드가 실행되기 위해선 자원(함수 실행 정보, 지역 변수 등)을 저장할 메모리가 필요하며, 이는 프로세스가 할당받은 메모리를 사용한다.
- 스레드는 프로세스 내에 존재하기에 프로세스의 자원을 공유하게 된다. 각자의 스레드는 Stack을 별도로 가지고 있게 되며 나머지 자원(Code, Data, Heap)을 공유한다.
- 만약 여러 스레드가 실행된다면 프로세스의 자원을 공유하기에 빠르게 자원을 가져다 쓸 수 있다.
- 반면 프로세스가 다른 프로세스의 자원을 사용하기 위해서는 IPC(Inter-Process Communication)라는 방식을 사용해야 한다.
- 프로그램을 실행하면 일반적으로 1개의 프로세스와 1개의 메인 스레드를 가지게 된다. 그러나 프로그램이 해야 할 태스크들이 많아지면 자연스럽게 여러 개의 스레드를 동시에 실행할 수 있으며, 혹은 여러 개의 프로세스를 동시에 운영할 수도 있다.
- 📘 프로세스와 스레드의 차이와 장단점을 비교하는 글이 많은데, 이것은 멀티 프로세스와 멀티 스레드를 선택해야 하는 기준에서 나온 장단점일 뿐, 실제로 프로세스와 스레드를 분리해서 비교하는 건 옳지 않다.
🦄 더 공부하면 좋을 내용
- 운영체제의 스케줄링 기법
- 하나의 머신에서 수많은 프로세스들을 효율적으로 관리하기 위해 다양한 스케줄링 기법들이 존재한다.
- 스레드의 구분 (유저 스레드와 커널 스레드)
- 실제로 스레드는 유저 스레드와 커널 스레드로 나뉜다. 이때 유저 스레드는 운영체제의 자원을 요구하기 위해 커널 스레드와 매핑이 되어있다.
- 다중 스레드 모델
- 다중 스레드의 경우 유저 스레드와 커널 스레드가 연결된 방식이 다양하다.
동시성과 병렬성
동시성 (Concurrency)
- 운영체제에서 하나의 CPU를 최대한 활용하여 여러 작업을 빠르게 수행한다. CPU는 한 번에 하나의 명령어만 처리하지만, CPU가 맡는 프로세스(스레드)가 Block 상태가 되면 빠르게 다음에 처리해야 할 프로세스가 CPU를 점유하게 된다. (CPU 입장에서는 여러 프로세스를 번갈아 가며 일을 하기 위해선 프로세스의 정보들을 메모리에 저장하고 불러와야 한다. 이때 CPU가 작업해야 할 프로세스를 바꾸는 일을 컨텍스트 스위칭이라고 한다.)
- CPU가 쉴 틈 없이 한 번에 주어진 태스크들을 빠르게 처리되다 보니 컴퓨터 사용자는 사실상 모든 프로세스의 명령이 “동시에“ 처리된다고 느끼게 된다. 이렇게 진행되는 현상을 동시성이라고 한다.
- 📘 동시성이라는 개념은 물리적으로 CPU 1개의 코어에서만 동작하는 개념은 아니다. 제한된 자원에서 여러 작업을 한 번에 실행시키려는 논리적인 개념이라고 보면 된다.
- 즉, 실제 제한된 자원에서 여러가지를 번갈아가면서 빠른 작업을 하는것이 동시성이다.
- CPU Bound vs I/O Bound
- CPU Bound : I/O 보다는 CPU를 더 많이 쓰는 작업을 말한다. ex) 머신러닝
- I/O Bound : CPU 보다는 I/O가 더 많은 작업을 말한다. ex) 크롤링 로직, DB와 연동하여 데이터 주고받는 로직, 웹, WAS 서버( WebApplicationServer)
병렬성 (Parallelism)
- 실제로 여러 개의 작업, 명령어를 동시에 처리하는 것
- 병렬성은 동시성을 품을 수도 있다. 여러 개의 CPU를 사용하면서 각각의 CPU에서 여러 태스크 (스레드나 프로세스)를 실행시키려고 한다면 동시성을 구현한 것이다.
🔑 정리
- 동시성(Concurrency)은 하나의 CPU로 여러 작업을 빠르게 처리하는 것
- 여러 작업이 한 번에 실행되는 것처럼 보이지만, 실제로는 하나의 CPU에서 처리하고 있는 것
- CPU 1 코어에 해당하는 물리적인 개념이 아닌, 제한된 자원에서 여러 작업을 한 번에 실행시키려는 논리적인 개념이다.
- 병렬성(Parallelism)은 여러 개의 CPU로 여러 작업을 빠르게 처리하는 것
- 여러 작업이 실제로 여러 개의 CPU를 통해 한 번에 처리된다.
멀티 스레드와 멀티 프로세스
멀티 스레딩
- 하나의 프로세스에서 하나의 스레드만 사용하는 것을 “싱글 스레딩“이라 한다. 대부분의 코드들은 싱글 스레드이다. (Node.js도 싱글 스레드 언어)
- 여러 개의 스레드를 사용하는 것을 “멀티 스레딩“이라 한다.
- 멀티 스레딩은 동시성과 병렬성을 구현하는 방법 중 하나이다. 컴퓨터가 하나의 CPU 코어만 가진 경우 “동시성“이 되고, 여러 개의 코어를 가진 경우 “동시성“과 “병렬성“ 둘 다 구현하는 셈이 된다.
- 멀티스레딩 사용 사례
- I/O Bounded 작업이 많은 경우 사용.
- I/O Bounded 작업은 일반적으로 외부에서 데이터를 가져와야 하기 때문에 그동안은 해당 cpu가 block 처리가 되어 있다.
- I/O 작업 기다리는 동안 다른 작업을 할 수 있도록 스레드를 여러개 만들면 일을 효과적으로 처리할 수 있다.
- 예를 들면 여러 사이트를 크롤링해야 하는 경우, 각 크롤링 작업은 대부분 I/O (해당 서버에 요청하고 응답받기 때문) 작업이기 때문에, 멀티 스레딩을 사용하기 좋다.
- I/O Bounded 작업이 많은 경우 사용.
- 📘 파이썬의 멀티 스레드는 사실 싱글 스레드처럼 동작한다
- 실제로 파이썬 내부적으로 GIL(Global Interpreter Lock)이라는 정책에 따라서 싱글 스레드처럼 동작한다. 이 글 (opens new window)을 참고
멀티 프로세싱
- 2 개 이상의 프로세스를 사용하는 것을 멀티 프로세싱 이라고 한다.
- 멀티 프로세싱도 동시성과 병렬성을 구현하는 방법 중 하나이다. 컴퓨터가 하나의 CPU 코어만 가진 경우 동시성이 되고, 여러 개의 코어를 가진 경우 동시성과 병렬성 둘 다 구현하는 셈이 된다.
- 멀티프로세싱 사용 사례
- 대표적인 경우는 독립적인 CPU Bounded 작업이 많은 경우 사용.
- CPU만 작업하는 머신러닝 코드에서 전처리하는 부분을 멀티프로세싱으로 풀어낼 수 있다.
멀티 스레드와 멀티 프로세스의 차이
- 멀티 스레드과 멀티 프로세스의 가장 큰 차이는, "메모리를 공유하는가?" 이다. 멀티 스레드는 여러 개의 스레드가 하나의 프로세스 안에서 같은 메모리를 사용한다. 반면 멀티 프로세스는 각 프로세스가 별도의 메모리 공간을 갖는다.
- 그래서 멀티 스레드는 하나의 프로세스 안에서 메모리를 공유하기 때문에 코드 짜기가 쉽다. 전역변수를 사용하면 쉽게 가져다가 쓸 수 있다. 하지만 멀티 프로세스의 경우 메모리가 공유되지 않기 때문에 구현이 좀 더 복잡할 수 있다.
- 멀티 스레드는 멀티 프로세스보다 적은 메모리 공간을 사용(메모리를 공유하기 때문에)하며, 스레드간 컨텍스트 스위칭도 빠르다. 하지만 같은 메모리에 있는 자원들을 공유하므로, 각 스레드에서 자원 관리에 주의해야 한다(동기화 문제). 또한 하나의 스레드가 장애 시 전체 프로세스에 영향을 준다.
- 멀티 프로세스는 멀티 스레드보다 더 많은 메모리 공간을 차지하고, 프로세스간 컨텍스트 스위칭도 스레드에 비하면 느린 편이다. 하지만 각각의 독립적인 메모리 공간을 가지기에 자원 관리를 더 쉽게 할 수 있다. 따라서 스레드에 비하면 시스템이 전체적으로 안정적이다.
- 보통 메모리를 공유해야 하며 간단하게 작업을 구현하고 싶은 경우 멀티 스레딩을 사용하고, 독립적으로 메모리를 가지고 안정적으로 관리하고 싶은 경우 멀티 프로세싱을 사용하는 경우가 많다.
🔑 정리
- 멀티 스레딩은 하나의 프로세스에서 여러 개의 스레드를 사용하는 것
- 스레드간 하나의 프로세스의 메모리 공간을 공유
- 멀티 프로세싱보다 메모리 공간을 적게 쓰고, 컨텍스트 스위칭도 빠름
- 하지만 동시성 문제와 안정성에 대한 단점이 있다.
- 멀티 프로세싱은 하나의 프로세스에서 여러 프로세스를 사용하는 것
- 각 프로세스는 독립된 메모리 공간을 사용
- 동시성과 안정성에 대한 장점을 가지지만, 멀티 스레딩보다 메모리 공간을 많이 쓰고, 컨텍스트 스위칭도 느림
- 멀티 스레딩과 멀티 프로세싱은 동시성과 병렬성의 구현 방법이다.
- 병렬성을 가지면서 동시성을 가질 수도 있다.
- 멀티 프로세싱을 하도록 구현함과 동시에, 각 프로세스에서 멀티 스레딩을 하도록 구현하면 병렬성과 동시성 모두 가져가는 셈
동기와 비동기, 블락과 논블락
동기와 비동기
- 동기(Synchronous)와 비동기(Asyncronous)는 두 작업의 작동 방식에 대한 내용이다.
- 동기(Synchronous)
- 동기 방식은 작업 A가 작업 B에게 작업 요청을 하고 작업 A가 작업 B가 작업을 끝낼 때까지 관심을 가지고 기다리는 방식. 요청을 했을 때 시간이 오래 걸리더라도 요청한 자리에서 결과를 줘야 한다.
- 우리가 만드는 대부분의 코드는 동기 방식의 코드라고 볼 수 있다. 동기 방식은 직관적이고 이해가 쉽다. 또한 설계가 비교적 단순하다.
- 비동기(Asynchronous)
- 비동기 방식은 작업 A가 작업 B에게 작업 요청을 하고 작업 A가 작업 B가 작업을 끝낼 때까지 관심을 버리고 기다리지 않는다. 즉, 요청과 결과가 동시에 일어나지 않는 것
- 비동기 방식은 동기보다 비직관적이고 이해하기도 어렵다. 또한 설계도 다소 복잡하다. 하지만 요청 결과를 받을 때까지 기다리지 않고 다른 작업을 수행할 수 있어 효율적이다.
- 📘 동기, 비동기는 프로그래밍 언어의 특성이 아니다. 예를 들어, 파이썬은 기본적으로 대부분 동기 기반의 코드지만, 내장 지원 라이브러리를 통해 비동기 코드를 작성할 수 있다. 반면 자바스크립트는 대표적인 비동기 언어로 소개되곤 하지만, 실제 동기 코드로도 많이 작성한다.
- 비동기 방식으로 코드를 작성하면, 응답에 대해서 처리를 하는 코드를 따로 작성해줘야 한다. 이때 콜백(Callback) 방식을 많이 활용한다.
블록과 논블락
- 블락(Block)과 논블락(Nonblock)은 "작업의 상태"에 대한 내용이다.
- 블락
- 일반적으로 함수 A가 함수 B를 호출하면, 프로세스의 제어권은 함수 B로 넘어가게 된다. 함수 B가 프로세스의 제어권을 가지고 있는 동안 함수 A는 아무것도 하지 않게 되는데 이 상태를 블락 상태에 있다고 말한다. 또 이런 함수 B를 블락킹 함수라고 말할 수 있다. 함수 B가 모두 실행되고, 프로세스의 제어권이 다시 함수 A로 오게 되면 함수 A의 "블락" 상태는 풀리게 된다.
- 논블락
- 함수 A에서 함수 B를 스레드로 생성하는 함수를 호출했다. 스레드를 생성하는 함수는 함수 B를 별도의 스레드로 생성하고, 특정 객체를 바로 리턴한다. 함수 A가 있는 스레드는 함수 호출 이후의 일을 계속해서 하게 된다. 즉 이 과정에서 함수 A는 "블락" 상태를 가지지 않는다. 이렇게 "블락" 상태를 가지지 않는 상태를 논블락 상태라고 한다. 또 이런 함수 B를 논블락킹 함수라고 부를 수 있다.
- 블락/논블락을 접하는 가장 대표적인 사례가 I/O 관련 코드를 작성할 때이다.
동기/비동기 vs 블락/논블락 차이
- 동기는 블락과, 비동기는 논블락과 비슷한 개념처럼 보인다.
- 동기/비동기는 한 작업에서 다른 작업의 작업 완료 여부에 관심이 있느냐에 있다.
- 즉 관심이 있다면 동기 작업, 관심이 없다면 비동기 작업
- 한편 블락/논블락은 한 함수에서 호출한 다른 함수가 바로 리턴을하여, 현재 진행 중인 함수의 프로세스 제어권을 가져가느냐 아니냐에 있다. 호출한 함수가 바로 리턴하지 않아, 프로세스 제어권을 뻇기게 되면 블락상태에 있게 되는 것. 반면 바로 리턴하게 된다면 논블락 상태에 있게 되는 것
- 📘 블락, 논블락과 달리 동기, 비동기는 추상적인 개념이다. 어떤 맥락에서는 블락, 논블락을 동기, 비동기라고 부를 수도 있다. 따라서 이 개념들을 너무 분리하여 외울 필요는 없다.
🔑 정리
- 동기와 비동기는 작업 완료에 관심이 있느냐에 관한 작동 방식
- 작업 A가 작업 B의 작업 완료에 관심이 있다면 동기
- 관심이 없다면 비동기
- 블락과 논블락은 프로세스 제어권을 뺏기는 상태에 대한 내용
- 함수 A가 함수 B를 호출하고 함수 B가 실행되는 동안 프로세스 제어권을 뺏겨 본인 로직을 실행하지 못하는 경우 블락
- 반면 프로세스 제어권을 뺏기지 않고 바로 리턴 받아 본인의 로직을 실행하면 논블락
- 일반적으로 동기/블락과 비동기/논블락 방식이 쓰임
- 동기/블락 방식은 이해하기 쉽고 직관적이지만 일반적으로 느리다.
- 비동기/논블락 방식은 이해하기 어렵고, 프로그램 흐름도 어려워지지만 일반적으로 빠르다.
- 동기, 비동기, 블락, 논블락의 차이점을 외우려고 하기보단, 맥락을 파악하는 정도면 충분
320x100
반응형
'Devlog > CS' 카테고리의 다른 글
객체 지향 프로그래밍이란? (0) | 2022.08.07 |
---|---|
객체지향 5대 원칙 - SOLID 원칙 (0) | 2022.08.06 |
테스트 코드와 TDD 이해하기 (0) | 2022.07.11 |
쿠키와 세션, 인증 이해하기 (0) | 2022.06.24 |
OSI 7계층과 TCP/IP 4계층 모델 (0) | 2022.06.22 |