들어가며
개인적으로 프로젝트를 진행 하다가 처리 시간이 긴 프로세스를 만난 적이 있었습니다. 그때 바로 생각난 개념이 “멀티 프로세싱” 이였습니다. 그런데 공부를 하다 보니 “스레드”라는 개념이 튀어나옵니다. “프로세스 vs 스레드” 끝인가? 아닙니다. 그리고 “동시성”, “병렬성” “뮤텍스”, “교착상태” 개념, 파이썬이면 “GIL”까지 튀어나오게 됩니다.
마음으로는 멀티 프로세스? 멀티 스레드? 무엇이든 당장 구현하고 싶었지만 이 개념들이 하루 만에 소화 되지도 않습니다.
특히 멀티 프로세스나 멀티 스레드는 유의해서 사용해야 한다는 정보가 많았기 때문에 선뜻 적용하기가 어려웠던 기억이 있습니다.
이번 기회에 소화가 안 되었던 개념들을 정리해볼 수 있을 것 같습니다.
물론 지금도 한 번에 모든 개념을 정리할 수는 없으므로 여러 개념 중에 오늘은 우선 “프로세스, 스레드”의 개념부터 정리해보았습니다.
중간에 모르는 개념이 있더라도 일단은 쓱 보고 지나가시면 좋을 것 같습니다.
프로세스
프로세스는 운영 체제에서 실행되는 프로그램의 인스턴스입니다. 여기에는 실행 중에 할당된 프로그램 코드, 데이터 및 리소스가 포함됩니다.
프로세스의 특징은 아래 테이블과 같습니다.
특징 | 설명 |
---|---|
식별자 | 운영 체제에서 할당한 고유한 숫자 식별자인 프로세스 ID(PID)로 식별됩니다. |
메모리 | 운영 체제에서 제공하는 코드, 데이터, 스택 및 힙을 포함하는 자체 주소 공간이 있습니다. |
프로세스 간 통신(IPC)* | 파이프, 공유 메모리, 메시지 큐, 소켓과 같은 다양한 메커니즘을 통해 다른 프로세스와 통신할 수 있습니다. |
수명주기 | 생성, 실행에서 시작하여 잠재적으로 종료로 끝나거나 운영 체제에서 정리를 기다리는 좀비 프로세스가 되는 수명 주기를 거칩니다. |
컨텍스트 전환 | 프로세스 간 전환 시 CPU 상태를 저장하고 복원하는 프로세스를 포함하며, 멀티태스킹을 용이하게 하기 위해 운영 체제의 스케줄러에 의해 관리됩니다. |
프로세스 만으로 작업을 처리할 때의 문제점
프로세스로만 작업을 처리하면 리소스 오버헤드, 통신 오버헤드, context switching 등에서 문제가 발생할 수 있습니다.
리소스 오버헤드
프로세스는 각각 고유한 메모리 공간, 파일 설명자 및 기타 리소스를 갖춘 중량급 엔터티입니다. 프로세스를 생성하고 관리하면 특히 메모리 소비 및 시스템 리소스 활용 측면에서 상당한 오버헤드가 발생합니다.
통신 오버헤드
파이프, 소켓, 공유 메모리 등의 IPC(프로세스 간 통신) 메커니즘은 프로세스가 데이터를 교환하고 활동을 동기화하는 데 필요합니다.
그러나 이러한 메커니즘은 스레드 기반 통신에 비해 추가적인 복잡성과 오버헤드를 발생시켜 시스템 확장성과 응답성에 영향을 줄 수 있습니다.
느린 컨텍스트 전환
프로세스 간 컨텍스트 전환에는 메모리 내용, 파일 설명자 및 CPU 레지스터를 포함한 전체 프로세스 상태를 저장하고 복원하는 작업이 포함됩니다.
공유 메모리 공간으로 인해 오버헤드가 적은 스레드 컨텍스트 전환에 비해 프로세스 컨텍스트 전환은 더 느리고 리소스 집약적인 경향이 있습니다.
오류 처리의 복잡성
다중 프로세스 환경에서 오류 및 예외를 관리하면 애플리케이션 개발 및 유지 관리가 더 복잡해집니다.
프로세스는 별도의 메모리 공간에서 작동하므로 프로세스 경계를 넘어 예외를 전파하고 오류를 정상적으로 처리하기가 어렵습니다.
스레드
스레드는 프로세스 내의 단일 순차적 제어 흐름을 나타냅니다.
특징 | 설명 |
---|---|
식별자 | 운영 체제의 스레드 관리 구현에 따라 프로세스 내에서 고유할 수도 있고 그렇지 않을 수도 있는 스레드 ID(TID)로 식별됩니다. |
메모리 | 프로세스 내의 스레드는 코드, 데이터, 힙을 포함하여 동일한 메모리 공간을 공유하므로 공유 변수 및 리소스에 효율적으로 액세스할 수 있습니다. |
커뮤니케이션 | 프로세스 내의 스레드는 공유 메모리를 통해 직접 통신하거나 잠금, 세마포어 또는 메시지 대기열과 같은 동기화 메커니즘을 통해 간접적으로 통신할 수 있습니다. |
수명주기 | 스레드는 생성, 실행, 잠재적으로 종료로 끝나는 프로세스와 유사한 수명 주기를 따르지만 스레드가 속한 프로세스의 컨텍스트 내에서 관리됩니다. |
컨텍스트 전환 | 프로세스 내에서 멀티태스킹을 용이하게 하기 위해 운영 체제의 스레드 스케줄러에서 관리하는 스레드 간 전환 시 CPU 상태를 저장하고 복원하는 작업이 포함됩니다. |
스레드의 자원 공유
스레드는 자원 중에 stack 만 따로 할당 받고 나머지 영역은 스레드끼리 서로 공유합니다
스레드가 해결한 프로세스의 문제들
리소스 오버헤드 감소
스레드는 프로세스보다 가볍기 때문에 생성 및 관리에 필요한 메모리와 시스템 리소스가 더 적습니다.
낮은 통신 오버헤드
동일한 프로세스 내의 스레드는 메모리를 공유하므로 프로세스 간 통신에 비해 더 효율적인 통신 및 데이터 공유가 가능합니다.
더 빠른 컨텍스트 전환
스레드 간 컨텍스트 전환은 프로세스 간보다 더 빠르고 리소스 집약적이지 않으므로 시스템 응답성이 향상됩니다.
단순화된 오류 처리
동일한 프로세스 내의 스레드는 예외 처리 메커니즘을 공유하고 별도의 메모리 공간에서 작동하는 프로세스보다 더 쉽게 오류를 전파할 수 있습니다.
프로세스, 스레드 주소공간
각 프로세스에는 다른 프로세스와 격리된 자체 메모리 공간이 있습니다.
이러한 격리는 데이터 무결성과 보안을 보장하지만 프로세스 간 통신을 위한 명시적인 통신 메커니즘이 필요합니다.
동일한 프로세스 내의 스레드는 동일한 주소 공간을 공유하므로 복잡한 통신 메커니즘 없이도 공유 데이터에 직접 액세스할 수 있습니다.
프로세스에는 독립적인 메모리 공간이 있지만 동일한 프로세스 내의 스레드는 공유 데이터에 동시에 액세스하고 수정할 수 있으므로 병렬 처리 작업에 적합합니다
멀티 프로세스와 멀티 스레드
멀티 프로세스는 하나의 운영체제 안에서 여러 프로세스가 실행되는 것을 의미 합니다.
멀티 쓰레드는 하나의 프로세스가 여러 작업을 여러 쓰레드를 사용해 동시에 처리하는 것을 의미합니다.
멀티 프로세스, 멀티 스레드 무엇을 선택?
강력한 격리와 내결함성을 요구하는 CPU 바인딩 작업에는 멀티프로세싱을 사용하고,
멀티스레딩은 I/O 바인딩 작업, 리소스 공유, 높은 수준의 동시성 및 응답성 달성에 더 적합하다고 합니다.
CPU 바인딩 작업이란?
CPU 바인딩 작업은 특정 작업이나 프로세스를 실행하기 위해 특정 CPU 코어를 할당하는 프로세스를 나타냅니다.
간단히 말해서, 이는 컴퓨터가 특정 작업을 수행하기 위해 두뇌의 특정 부분(CPU 코어)만 사용하도록 지시하는 것과 같습니다.
높은 수준의 동시성 및 응답성이 필요한 경우
여러 작업을 동시에 수행해야 하고 시스템이 사용자 입력이나 외부 이벤트에 신속하게 반응해야 하는 시나리오에서는 높은 동시성과 응답성이 매우 중요합니다.
이를 통해 지연이나 중단 없이 작업을 효율적으로 실행할 수 있습니다.
예를 들면, 제어 시스템이나 금융 거래 플랫폼과 같이 실시간 처리가 필요한 시스템은 이벤트를 신속하게 처리하고 대응하기 위해 높은 동시성을 필요로 합니다.
마찬가지로, 비디오 게임이나 멀티미디어 소프트웨어와 같은 대화형 애플리케이션은 원활하고 몰입감 있는 사용자 경험을 제공하기 위해 높은 응답성을 요구합니다.
멀티 스레딩 시 유의사항
데이터 손상 및 경합 상태를 방지하기 위해 여러 스레드에서 공유 데이터 구조 및 리소스에 안전하게 액세스하는지 확인해야 합니다.
잠금, 뮤텍스 또는 원자성 작업과 같은 동기화 메커니즘을 사용하여 공유 리소스에 대한 액세스를 조정해야 합니다.
마치며
프로세스와 스레드의 개념, 그리고 이들이 사용하는 주소 공간, 멀티 프로세스와 멀티 스레드의 차이까지 정리해보았습니다.
역시 멀티 프로세스, 멀티 스레드 까지 정리하려니 뮤텍스, 동기화 메커니즘, 교착 상태, 컨텍스트 스위칭 등 너무 다양한 개념들이 다시 꼬리를 물고 나타납니다.
일부는 들어 보았지만 정리가 안 된 개념, 그리고 일부는 다행히 이전에 정리한 개념들이 섞여 있네요.
다음 포스트에서는 이번에 못 정리한 개념들을 다시 다뤄보도록 하겠습니다.
참고하면 좋은 글
When and How to use MultiProcessing and Multi-Threading in Python (morioh.com)