동기화?
왜 동기화를 알아야 할까?
동기화는 백엔드 개발자가 안정적이고 일관된 시스템을
구축하기 위해 필수적이기 때문이다.
동기화가 없으면 여러 쓰레드나 프로세스가 동시에 자원에 접근할 때
데이터 불일치나 충돌이 발생할 수 있다.
예를 들어, 온라인 쇼핑몰에서 재고를 관리할 때 동기화가 이루어지지 않으면
중복 구매나 재고 초과 문제가 생길 수 있다.
분산 시스템이나 멀티쓰레드 환경에서는
동기화를 통해 데이터의 일관성과 정확성을 보장해야 한다.
동기화는 또한 시스템 성능을 최적화하고
자원을 효율적으로 활용하는 데 중요한 역할을 한다.
결국, 동기화는 안정적이고 신뢰할 수 있는 서비스를
개발하기 위한 백엔드 개발자의 핵심 역량 중 하나다.
동기화
아무렇게나 마구 실행해도 괜찮을까?
동기화는 여러 작업(프로세스나 쓰레드)이 공유 자원에 접근할 때 수행 시기를 조율하는 것을 의미한다. 예를 들어, 두 사람이 동시에 하나의 문서를 편집한다고 상상해보자. 한 사람이 문서를 저장하는 순간, 다른 사람이 동시에 수정한다면 결과물이 엉망이 될 수 있다.
동기화는 이런 혼란을 방지하고, 각 작업이 올바른 순서와 시점에 실행되도록 보장하는 역할을 한다.
즉, "프로세스들의 수행 시기를 맞추는 것"이 동기화의 핵심이다. 이를 통해 데이터의 일관성을 유지하고, 충돌 없이 안정적인 시스템 운영이 가능해집니다. 동기화는 결국 작업 간의 질서를 만들어 시스템이 마치 하나의 유기체처럼 정확히 동작하도록 돕는 중요한 개념입니다.
동기화 핵심개념
상호 배제 (Mutual Exclusion)
여러 프로세스가 동시에 공유자원에 접근할 때 충돌을 방지하기 위해 한번에 하나의 프로세스만 자원에 접근하도록 제한 하는 동기화 방식
-> ex) 파일 쓰기 작업에서 두 프로세스가 동시에 파일에 접근하면 데이터가 손상될 수 있으므로, 한번에 한 프로세스만 파일에 접근하도록 해야 한다.
bank account problem
- 공유자원?
여러 프로세스나 쓰레드가 동시에 접근하거나 사용하는 자원으로, 메모리, 파일, 데이터베이스 등이 해당됨.
실행 순서 제어
여러 프로세스가 협력하여 작업을 수행할 때, 올바른 순서로 실행 되도록 보장하는 동기화 방식이다.
-> ex) 생산자- 소비자 문제에서 생산자가 데이터를 생성한 후에만 소비자가 이를 처리하도록 순서를 제어해야 한다.
reader writer problem
공유자원과 임계구역
공유자원
공유 자원(Shared Resource)은 여러 프로세스나 쓰레드가 동시에 사용하거나 접근하는 자원을 의미한다.
- ex ) 메모리 공간, 파일, 데이터베이스, 네트워크 연결
- 공유 자원은 효율적인 시스템 운영을 위해 여러작업이 협력하여 사용해야 하지만, 동시에 접근하면 Race Condition이 발생할 수 있다.
-Race Condition?
두 개 이상의 작업이 동시에 공유 자원에 접근하거나 수정하려고 할 때, 실행 순서나 타이밍에 따라 예상치 못한 결과가 발생하는 상황
임계구역
임계구역(Critical Section)은 공유자원에 접근하는 코드 영역으로, 데이터 충돌이나 무결성 문제가 발생할 수 있는 부분을 말한다.
- 공유자원에 접근하는 작업이 포함된다.
- 동시에 여러 프로세스나 쓰레드가 접근하면 문제가 발생할 수 있다.
- 반드시 동기화(Synchronization)를 통해 제어해야한다.
class Counter {
private int count = 0;
// 임계구역
public synchronized void increment() {
count++; // 공유 자원 count에 접근
}
public synchronized int getCount() {
return count;
}
}
운영체제가 임계구역 문제를 해결하는 세가지 원칙
운영체제가 임계구역 문제(Critical Section Problem) 를 해결하기 위해 따라야 하는 세 가지 주요 원칙은 다음과 같다. 이 원칙들은 공유 자원에 대한 동시 접근으로 인해 발생할 수 있는 문제를 방지하고, 시스템의 안정성과 신뢰성을 보장한다.
상호 배제(Mutual Exclusion)
- 한 번에 하나의 프로세스만 임계구역에 진입할 수 있어야 합니다.
- 다른 프로세스가 이미 임계구역에 진입해 있는 경우, 다른 모든 프로세스는 대기 상태가 되어야 합니다.
- 목적: 공유 자원에 대한 동시에 접근을 방지하여 데이터의 무결성을 유지.
진행(Progress)
- 임계구역에 진입하고자 하는 프로세스가 없으면, 진입을 시도하는 프로세스 중 하나가 반드시 선택되어야 합니다.
- 선택 과정은 무작위가 아닌, 참여하는 프로세스들이 결정에 영향을 미칠 수 있어야 합니다.
- 목적: 불필요한 대기로 인해 시스템이 정체되지 않도록 보장.
유한 대기(Bounded Waiting)
- 각 프로세스가 임계구역에 진입하기 위해 기다려야 하는 최대 대기 시간이 제한되어야 합니다.
- 특정 프로세스가 영원히 기다리는 기아 상태(Starvation)를 방지해야 합니다.
- 목적: 모든 프로세스에 공정한 접근을 보장.
동기화 기법
동기화 기법은 멀티스레드 또는 멀티프로세스 환경에서 공유 자원의 안전한 접근을 보장하기 위한 도구다. 동기화 기법은 경쟁 상태(race condition)를 방지하고, 여러 프로세스가 동시에 자원에 접근할 때 발생할 수 있는 데이터 무결성 문제를 해결할 수 있는 방법이다. 대표적인 동기화 기법으로 뮤텍스 락(Mutex Lock), 세마포어(Semaphore), 모니터(Monitor)가 있다.
뮤텍스 락
뮤텍스는 상호 배제를 보장하는 동기화 기법이다. 뮤텍스 락을 사용하면, 한번에 하나의 프로세스만 공유 자원에 접근할 수 있다. 뮤텍스락은 공유자원에 동시에 접근하는 것을 막는 일종의 문(lock) 이라고 생각하면 된다. 락은 자원에 대한 독점적인 접근을 보장한다. 즉, 한 프로세스만 자원을 사용할 수 있도록 잠금을 걸어놓고, 다른 프로세스나 스레드는 그 자원을 사용할 수 없게 대기하게 만든다.
간단하고 효과적이지만 락을 적절히 해제하지 않으면 교착상태나 기아상태가 발생한다.
쉽게 이야기해서 옷가게에 존재하는 피팅룸을 생각해보자. 하나의 피팅룸이 존재할 때 그 피팅룸 안에는 한사람이 들어가게되고 독점적으로 사용한다. 피팅룸을 들어갈 때 문을 잠그면 다른 사람들은 접근할 수 없게된다. 또한 사람이 나오기 위해서는 락을 해제해야한다. 이러한 원리가 뮤텍스락과 일치한다.
교착상태?
- 두 개 이상의 프로세스가 서로의 자원을 기다리며 영원히 실행되지 않는 상태
세마포어
세마포어(Semaphore)는 공유 자원에 대한 접근을 제어하는 동기화 기법으로, 자원의 수를 추적하고 동시에 자원을 사용할 수 있는 프로세스의 수를 제한하는데 사용된다. 세마포어는 카운팅 세마포어(Counting Semaphore)와 이진 세마포어(Binary Semaphore)로 나뉜다.
세마포어는 자원의 수를 제어하면서 동시 접근을 제한한다. 카운팅 세마포어는 여러 자원에 대한 접근을 동시에 처리할 수 있게 해주고, 이진 세마포어는 하나의 자원에 대한 접근을 제어한다.
세마포어는 하나이상의 자원을 동시에 제어할 수 있어 다수의 자원을 관리하는데 유용하고, 여러프로세스가 동시에 자원을 사용할 수 있도록 하여 시스템 자원의 활용도를 높일 수 있다.
하지만 잘못된 세마포어 값 관리로 인해 데드락이나 기아상태가 발생할 수 있다. 또한 세마포어를 잘못 사용하면 자원에 대한 불완전한 접근이나 경쟁상태가 발생할 수 있다.
또한, 세마포어는 자원관리를 수동으로 해야하기에 복잡한 상황에서는 세밀한 제어가 필요하다.
- 세마포어의 종류
- 이진 세마포어(Binary Semaphore): 값이 0 또는 1인 세마포어로, 주로 뮤텍스 락과 유사한 역할을 한다. 한 번에 하나의 프로세스만 자원을 사용하도록 제한한다.
- 카운팅 세마포어(Counting Semaphore): 여러 개의 자원을 관리할 수 있으며, 자원의 수에 따라 양의 정수 값을 가진다. 예를 들어, 프린터가 3대 있을 때, 카운팅 세마포어의 값은 3이 될 수 있다.
세마포어는 P() (대기, acquire())와 V() (신호, release()) 연산을 통해 작동한다.
- P(): 자원 사용을 요청하는 연산으로, 세마포어 값을 감소. 값이 0이면 대기.
- V(): 자원 사용을 종료하는 연산으로, 세마포어 값을 증가. 대기 중인 프로세스를 깨울 수 있다.
모니터
모니터(Monitor)는 동기화 기법 중 하나로, 공유 자원에 대한 안전한 접근을 보장하고 상호 배제를 제공하는 고수준의 추상화다. 모니터는 상호 배제(Mutual Exclusion)와 동기화를 손쉽게 처리할 수 있게 해주는 개념으로, 뮤텍스 락과 세마포어와 같은 저수준 동기화 기법을 사용하지 않고도 자원에 대한 안전한 접근을 보장한다. 모니터는 주로 스레드의 동기화를 관리하는 기법이다.
모니터내에서 자원에 대한 접근은 자동으로 상호 배제가 보장되고, 고수준의 동기화 매커니즘을 제공하여 동기화를 간단하게 처리할 수 있다. 이를 통해, 코드의 복잡도를 낮추고, 동기화가 필요한 부분을 효율적으로 관리할 수 있다. 또한 모니터는 조건변수(Condition Variable) 을 사용해 대기중인 프로세스를 효율적으로 관리할 수 있다. 프로세스가 자원을 사용할 수 없을 때 대기하고, 자원이 준비되면 다른 프로세스를 깨울 수 있어 자원 사용을 최적화 할 수 있다.
하지만, 모니터는 지원되는 언어에서만 사용할 수 있으며, 조건변수와 같은 기능을 포함하여 복잡한 동기화 상황에서는 관리가 어려울 수 있다. 이로서 잘못된 대기와 신호 처리로 인해 기아상태나 교착상태가 발생할 수 있다. 특히, 프로세스간 자원 배분을 잘못하면 이러한 문제가 심화될 수 있다.
모니터의 구성요소
- 상호 배제를 보장하는 메서드:
- 모니터 내부의 메서드들은 상호 배제를 보장하며 동시에 한 프로세스만 자원에 접근할 수 있도록 한다.
- 한 프로세스가 자원을 사용할 때 다른 프로세스는 대기하거나 대기열에 추가.
- 조건 변수(Condition Variable):
- 조건 변수는 대기 중인 프로세스를 처리하는 데 사용된다. 대기 조건이 만족되었을 때 대기 중인 프로세스를 깨울 수 있다.
- 예를 들어, 자원이 사용 가능해졌을 때 대기 중인 프로세스를 깨워 자원을 사용할 수 있게 한다.
- 조건 변수는 wait(), signal(), broadcast() 와 같은 연산을 제공하여 대기와 신호를 관리한다.