Philosophers는 동시성과 교착상태를 설명하는 대표적인 예시인 "식사하는 철학자 문제"를 직접 구현해보는 과제이다. 특히, Mandatory 파트는 멀티 스레드 환경에서 공유 자원에 대한 상호 배제 달성을 위해 뮤텍스를 사용해야 한다.
과제를 시작하기 전 알아야 할 사전 지식에 대해 알아보겠다 🥸
P.S. 왜 멀티 스레드 환경에서 공유 자원에 대한 관리가 필요한지, 관리를 위한 방법에는 무엇이 있는지 정도를 알아보는 매우 간단한 글입니다
1. Thread
✨ 프로세스란?
실행 중인 프로그램이다. 즉, 사용자가 작성한 프로그램이 운영체제에 의해 메모리 공간을 할당받아 실행 중인 것을 의미한다. 이러한 프로세스는 프로그램에 사용되는 데이터와 메모리 등의 자원 그리고 스레드로 구성된다.
✨ 스레드란?
프로세스 내에서 실제로 작업을 수행하는 주체를 의미한다. 모든 프로세스에는 한 개 이상의 스레드가 존재하여 작업을 수행한다. 또한, 두 개 이상의 스레드를 가지는 프로세스를 멀티스레드 프로세스라고 한다.
⚖️ 프로세스 vs 스레드
- 프로세스 : 멀티 프로세스에서 각 프로세스는 독립적으로 실행되며, 각각 별도의 메모리를 차지한다.
- 스레드 : 멀티 스레드는 프로세스 내의 메모리를 공유해 사용한다.
👍🏻 스레드의 장점
- 하나의 프로세스 내의 자원(코드, 데이터, 힙)을 공유하기 때문에 프로세스에 비해 자원 할당 비용이 적게 들고, 문맥 교환(context swith) 비용이 적게 듦
- 멀티 스레드의 경우, 일부 스레드의 처리가 지연되더라도 다른 스레드에서 작업을 계속 처리할 수 있음
👎🏻 스레드의 단점
- 하나의 스레드에서 발생한 문제가 프로세스 전반에 영향을 미칠 수 있음
- 멀티 스레드의 경우, 각각의 스레드 중 어떤 것이 먼저 실행될지 그 순서를 알 수 없고, 자원을 공유하기 때문에 문제가 발생할 수 있음
- 예시 : 두 스레드가 특정 공유 변수 i의 값을 1 증가시키는 명령을 실행할 때, 다음과 같은 방식으로 수행될 수 있다.
- 공유되는 변수 i의 값을 레지스터에 저장
- 레지스터의 값을 1 증가시킨다.
- 변수 i에 그 값을 저장한다.
스레드 동작 i의 값 스레드 1의 레지스터 스레드 2의 레지스터 스레드 1 i의 값을 레지스터에 저장 0 0 스레드 1 레지스터 값을 1 증가 0 1 스레드 1 i에 값 저장 1 1 스레드 2 i의 값을 레지스터에 저장 1 1 1 스레드 2 레지스터 값을 1 증가 1 1 2 스레드 2 i에 값 저장 2 1 2 스레드 동작 i의 값 스레드 1의 레지스터 스레드 2의 레지스터 스레드 1 i의 값을 레지스터에 저장 0 0 스레드 2 i의 값을 레지스터에 저장 0 0 0 스레드 1 레지스터 값을 1 증가 0 1 0 스레드 2 레지스터 값을 1 증가 0 1 1 스레드 1 i에 값 저장 1 1 1 스레드 2 i에 값 저장 1 1 1
- 예시 : 두 스레드가 특정 공유 변수 i의 값을 1 증가시키는 명령을 실행할 때, 다음과 같은 방식으로 수행될 수 있다.
2. Mutex
📌 교착상태 (Dead lock)
교착상태은 두 개 이상의 작업이 서로 상대의 작업이 끝나기만을 기다리고 있기 때문에 결과적으로 아무것도 완료하지 못하는 상태를 말한다.
📌 상호배제 (Mutual Exclusion)
하나의 프로세스가 공유 자원을 사용할 때 다른 프로세스가 동일한 공유 자원에 접근할 수 없도록 하는 것이다.
📌 임계 영역 (Critical Section)
여러 프로세스가 데이터를 공유하며 수행될 때, 각 프로세스에서 공유 데이터에 접근하는 프로그램 블록이다. 즉, 여러 프로세스가 동일 자원을 동시에 참조하여 값이 오염될 위험 가능성이 있는 영역을 말한다.
프로세스 간 메시지를 전송하거나, 공유 메모리를 통해 공유된 자원에 여러 개의 프로세스가 동시에 접근하면 Critical Section 문제가 발생할 수 있다. 이를 해결하기 위해 데이터를 한 번에 하나의 프로세스만 접근할 수 있도록 제한을 두는 동기화 방식을 취해야 한다.
이러한 동기화 도구에는 대표적으로 뮤텍스(mutex)와 세마포어(semaphore)가 있다.
✨ 뮤텍스란?
동시 프로그래밍에서 공유 불가능한 자원의 동시 사용을 피하기 위해 사용
- 동기화 대상이 1개
- 임계 구역을 가진 스레드들의 실행 시간이 서로 겹치지 않고 각각 단독으로 실행(상호배제)되도록 하는 기술
- 한 프로세스에 의해 소유될 수 있는 key를 기반으로 한 상호배제 기법
- key에 해당하는 어떤 객체가 있으며, 이 객체를 소유한 스레드/프로세스만이 공유 자원에 접근할 수 있음
- 다중 프로세스들의 공유 리소스에 대한 접근을 조율하기 위해 동기화(synchronization) 또는 락(lock)을 사용
- 즉, 뮤텍스 객체를 두 스레드가 동시에 사용할 수 없다.
✨ 세마포어란?
멀티 프로그래밍 환경에서 공유된 자원에 대한 접근을 제한하는 방법
- 동기화 대상이 1개 이상
- 현재 공유 자원에 접근할 수 있는 스레드/프로세스의 수를 나타내는 값을 두어 상호배제를 달성
- 공유 자원에 접근할 수 있는 스레드/프로세스의 최대 수만큼 동시에 접근할 수 있으며, 각 스레드/프로세스는 세마포어의 값을 확인하고 변경할 수 있음
- 자원을 사용하지 않는 상태가 될 때, 대기하던 스레드/프로세스가 즉시 자원을 사용하고 이미 다른 스레드/프로세스에 의해 사용 중이라는 사실을 알게 되면 재시도 전에 일정 시간 대기해야 함
- 일반적으로 비교적 긴 시간을 확보하는 리소스에 대해 사용
⚖️ 뮤텍스 vs 세마포어
- 가장 큰 차이점은 동기화 대상의 개수이다.
- 뮤텍스는 동기화 대상이 1개일 때 사용
- 세마포어는 동기화 대상이 1개 이상일 때 사용
- 세마포어는 뮤텍스가 될 수 있지만, 뮤텍스는 세마포어가 될 수 없다.
- 뮤텍스는 0, 1로 이루어진 이진 상태를 가지므로 이진 세마포어 !
- 뮤텍스는 자원 소유 가능 + 책임을 가지는 반면, 세마포어는 자원 소유 불가
- 뮤텍스는 0, 1뿐이므로 lock을 가질 수 있음
- 뮤텍스는 소유하고 있는 스레드만이 해당 뮤텍스를 해제할 수 있다. 반면, 세마포어는 해당 세마포어를 소유하지 않은 스레드도 해제할 수 있다.
- 세마포어는 시스템 범위에 걸쳐 있고, 파일 시스템 상의 파일로 존재한다. 반면, 뮤텍스는 프로세스의 범위를 가지며 프로세스가 종료될 때 자동으로 clean up된다.
'42Seoul' 카테고리의 다른 글
[Philosophers] 🚨 usleep 함수의 문제점 ? (0) | 2023.05.09 |
---|---|
[Philosophers] ⚖️ pthread_join vs pthread_detach (2) | 2023.05.09 |
[Philosophers] 1. Subject 이해와 허용 함수 정리 (3) | 2023.04.13 |