OS가 프로그램들 싸움 말리는 법
기본 개념
공유 자원
여러 프로그램이 서로 쓰겠다고 경쟁하는 것들이다.
- 하드웨어: CPU, 메모리
- 소프트웨어: 파일이나 DB
아무런 제어 없이 접근하면 Race Condition이 생긴다. 여러 사람이 동시에 화장실에 들어가려는 것과 같은 상황이다.
임계 영역
임계 영역은 공유 자원을 쓰는 코드 부분이다. 은행 잔액을 수정하는 코드가 대표적인 예다. 여기서 순서가 꼬이면 데이터가 망가진다.
동기화 도구들
뮤텍스
가장 단순한 해결책이다. 화장실 열쇠가 하나뿐인 것처럼, 한 스레드만 임계 영역에 들어갈 수 있다.
- 잠금/해제 두 가지 상태만 존재
- 잠금을 건 스레드만 해제 가능
- 단점: 대기 중인 스레드가 CPU를 계속 소비하는 바쁜 대기(busy waiting) 문제
세마포어
뮤텍스보다 유연한 방식이다.
- Binary: 0/1 두 가지 값만. 뮤텍스처럼 동작한다.
- Counting: 정해진 수까지 동시 접근을 허용한다. 주차장 자리를 세는 것과 비슷하다.
뮤텍스와 달리 잠금을 건 스레드가 아닌 다른 스레드가 해제할 수 있다.
모니터
세마포어보다 고수준의 동기화 도구다. 실수할 여지가 줄어들고 Java의 synchronized 키워드가 이 개념을 구현한 것이다.
실제 사용
DB에서
flowchart TB A[이체 요청] --> C{락 관리자} B[잔액 확인] --> C C --> D[순서대로 처리]
멀티스레드에서
flowchart LR A[파일 저장] --> C[임계 영역] B[파일 읽기] --> C C --> D[순차 처리]
자주 생기는 문제
교착상태 (Deadlock)
서로 상대방이 가진 자원이 해제되기를 기다리며 멈춰버리는 상태다. 해결 방법:
- 자원 할당 자체를 제한해 애초에 발생하지 않게 막기
- 교착 상태가 탐지되면 특정 프로세스를 강제 종료하기
- 주기적으로 시스템 상태를 체크해서 교착 상태 발생 시 복구하기
기아 상태 (Starvation)
우선순위가 낮은 스레드가 계속 자원을 할당받지 못하는 상태다. 에이징(aging) 기법으로 해결한다. 오래 기다린 프로세스의 우선순위를 점차 높여주는 방식이다.
실전 팁
- 임계 영역은 가능한 짧게 유지하기
- 중첩 잠금은 데드락의 원인이 되므로 피하기
- 직접 구현보다 검증된 라이브러리나 언어 기본 메커니즘 활용하기
최신 동향
- 락프리(Lock-free): CAS(Compare-And-Swap) 연산 등을 활용해 잠금 없이 동시성을 처리하는 방식
- 트랜잭셔널 메모리: DB 트랜잭션처럼 메모리 접근을 처리하는 방식
- 동시성 프레임워크: Java의
java.util.concurrent처럼 잘 만들어진 추상화 활용