배경
#1 디버깅 과정에서 정리한 Python 멀티스레딩 학습 내용.
GIL이 있어도 레이스 컨디션은 발생한다
GIL이 보장하는 것
- 단일 바이트코드 명령어의 원자성 (예:
STORE_SUBSCR — dict[key] = value)
- interpreter 내부 자료구조의 무결성 (crash 방지)
GIL이 보장하지 않는 것
check-then-act 패턴
# 이 코드는 3개의 바이트코드 명령어로 컴파일된다
if state["pressed"] and not state["holding"]: # ① 읽기 (LOAD)
state["holding"] = True # ② 쓰기 (STORE)
on_start() # ③ 부수효과 (CALL)
GIL은 ①과 ② 사이에서 스레드를 전환할 수 있다. 다른 스레드가 이 틈에 같은 state를 변경하면 논리적 불일치가 발생한다.
GIL 전환 조건
- 5ms 간격:
sys.getswitchinterval() 기본값 = 0.005초
- I/O 블로킹: 파일, 네트워크, sleep 등 호출 시 즉시 전환
안전/위험 판단 기준
| 안전 (단일 명령) |
위험 (복합 연산) |
state["x"] = 1 |
if state["x"]: state["y"] = ... |
dict[key] 단일 읽기 |
여러 키를 읽고 그 결과로 분기 |
list.append(x) |
if x not in list: list.append(x) |
결론
GIL은 interpreter crash 방지이지, 비즈니스 로직의 원자성 보장이 아니다. check-then-act 패턴은 언어와 무관하게 항상 lock이 필요하다.
참고
- CPython GIL 문서
dis 모듈로 바이트코드 확인 가능: python -m dis -c "d={'a':1}; d['a']=2"
배경
#1 디버깅 과정에서 정리한 Python 멀티스레딩 학습 내용.
GIL이 있어도 레이스 컨디션은 발생한다
GIL이 보장하는 것
STORE_SUBSCR—dict[key] = value)GIL이 보장하지 않는 것
check-then-act 패턴
GIL은 ①과 ② 사이에서 스레드를 전환할 수 있다. 다른 스레드가 이 틈에 같은 state를 변경하면 논리적 불일치가 발생한다.
GIL 전환 조건
sys.getswitchinterval()기본값 = 0.005초안전/위험 판단 기준
state["x"] = 1if state["x"]: state["y"] = ...dict[key]단일 읽기list.append(x)if x not in list: list.append(x)결론
GIL은 interpreter crash 방지이지, 비즈니스 로직의 원자성 보장이 아니다. check-then-act 패턴은 언어와 무관하게 항상 lock이 필요하다.
참고
dis모듈로 바이트코드 확인 가능:python -m dis -c "d={'a':1}; d['a']=2"