동기화 이슈(Synchronisation Issue)는 여러 스레드 또는 프로세스가 동시에 같은 자원(예: 메모리, 파일, 데이터베이스 등)에 접근하거나 조작할 때 발생하는 문제이다. 동기화가 제대로 처리되지 않으면 데이터의 무결성이 깨지거나, 프로그램의 동작이 비정상적으로 될 수 있다.
동기화 이슈가 발생하는 이유
- 자원 공유:
- 여러 스레드 또는 프로세스가 하나의 공유 자원(변수, 파일 등)에 동시에 접근할 때, 누가 먼저 작업을 수행할지 명확하지 않음.
- 경쟁 상태(Race Condition):
- 여러 스레드가 동일한 자원에 접근하여 값을 읽고, 수정, 저장하는 과정에서 수행 순서에 따라 결과가 달라지는 상황.
- 원자성 부족:
- 특정 작업이 중간에 다른 작업으로 끼어들면서, 작업 단위(원자성)가 보장되지 않아 데이터가 손상됨.
- 비결정적 동작:
- 병렬 환경에서는 실행 순서가 매번 달라질 수 있어, 실행 결과를 예측하기 어려움.
동기화 이슈의 종류
경쟁 상태(Race Condition)
- 여러 스레드가 동시에 자원에 접근하고, 그 결과가 실행 순서에 따라 달라지는 현상.
counter = 0
def increment():
global counter
for _ int range(1000):
counter += 1
thread1= threading.Thread(target=increment)
thread2= threading.Thread(target=increment)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(counter)
- 이유:
counter += 1이 내부적으로 읽기 → 수정 → 쓰기로 나뉘는데, 두 스레드가 동시에 실행되면 중간 상태가 덮어씌워질 수 있음.
데드락(교착 상태, Deadlock)
- 두 개 이상의 스레드 또는 프로세스가 서로 자원을 대기하면서, 무한 대기 상태에 빠지는 문제.
- 예:
- 스레드 A가 자원 1을 점유한 상태에서 자원 2를 기다리고,
- 스레드 B가 자원 2를 점유한 상태에서 자원 1을 기다리는 경우.
- 해결 방법:
- 자원을 일정한 순서로 요청(예: 항상 자원 1 → 2 순서로 요청).
- 타임아웃을 설정해 대기 시간이 초과되면 요청 취소.
라이브락(Livelock)
- 데드락과 비슷하지만, 프로세스가 계속 자원 상태를 변경하며, 진행하지 못하고 서로 방해하는 상태.
- 차이점: 데드락은 멈춰 있지만, 라이브락은 계속 실행되나 진전이 없음.
기아(Starvation)
- 특정 스레드나 프로세스가 자원을 요청했지만, 다른 작업들에 의해 계속 우선순위에서 밀려 자원을 얻지 못하는 상태.
- 발생 원인:
- 우선순위 기반 스케줄링에서 낮은 우선순위 작업이 무한 대기 상태가 됨.
- 해결 방법:
- 우선순위 상향 조정(Priority Inversion): 대기 시간이 길어질수록 우선순위를 높이는 방식.
비교적 어려운 디버깅
- 스레드 간의 실행 순서가 매번 달라질 수 있어, 오류가 비결정적(Non-deterministic)으로 발생.
- 이를 Heisenbugs라고 부르며, 디버깅이 어렵다.
동기화 이슈 해결 방법
뮤텍스(Mutex, Mutual Exclusion)
- 공유 자원에 한 번에 하나의 스레드만 접근하도록 잠금(Lock)을 설정.
import threading
counter = 0
lock = threading.Lock()
def increment():
global counter
for _ in range(1000):
with lock:
counter += 1
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(counter)
세마포어(Semaphore)
- 특정 자원을 사용할 수 있는 동시 접근 허용 수를 제한.
- 예: 데이터베이스 연걸 제한, 네트워크 포트 제한.
모니터(Monitor)
- 동기화를 위해 뮤텍스와 조건 변수를 조합한 고수준 동기화 도구.
- 대부분의 프로그래밍 언어에서 기본 제공(Java
synchronised, Python Condition).
원자 연산(Atomic Operation)
- 여러 단계를 하나의 단위로 묶어 중간 상태 없이 실행.
- 예:
- Python의
queue.Queue는 스레드 안전(Thread-safe)한 큐를 제공.
- C++의
std::atomic 클래스 사용.
읽기-쓰기 잠금(Read-Write Lock)
- 읽기 작업은 동시에 허용하지만, 쓰기 작업은 단독으로 실행.
- 적용 사례:
동기화 이슈의 예방 전략
- 자원 할당 순서 정의:
- 모든 스레드가 자원을 일정한 순서로 요청하여 데드락 방지.
- 타임아웃 설정:
- 자원 요청 시 제한 시간을 설정하여 무한 대기 방지.
- 경량 동기화 기법 사용:
- 자주 동기화가 필요한 경우, 무거운 잠금 대신 경량화를 고려(예: Spinlock).
- 스레드 수 관리:
- 필요한 만큼의 스레드만 생성하여 과도한 자원 경합 방지.
정리
- 동기화 이슈는 경쟁 상태, 데드락, 라이브락, 기아 등 여러 형태로 나타난다.
- 이를 해결하기 위해 뮤텍스, 세마포어, 원자 연산, 모니터 등 다양한 동기화 기법이 사용된다.
- 올바른 동기화 도구를 선택하고, 코드 설계를 신중히 하면 동기화 문제를 예방할 수 있다.