파이썬에서 동시성,병렬성에 대한 학습을 하면서
이해했던 것들을 재검토하는 시간에
Thread 안에 subprocess를 만나면 블로킹을 하고 다음 Thread로 넘어가는지에 대한 궁금증이 생겼다
바로 코드로 확인해보았다
import threading
import subprocess
import time
def run_command():
print("Starting command...")
subprocess.run(["sleep", "5"]) # 5초 동안 sleep하는 외부 명령
print("Command finished.")
def do_other_stuff():
for i in range(3):
print("Doing other stuff...")
time.sleep(1)
run_command()
do_other_stuff()
'''
Starting command...
Command finished.
Doing other stuff...
Doing other stuff...
Doing other stuff...
'''
우선 Thread를 사용하지 않고 나온 결과값은 순차적으로 잘 실행됨을 볼 수 있었다
다음엔 Thread으로 각각의 함수를 실행하는 코드이다
import threading
import subprocess
import time
def run_command():
print("Starting command...")
subprocess.run(["sleep", "5"]) # 5초 동안 sleep하는 외부 명령
print("Command finished.")
def do_other_stuff():
for i in range(3):
print("Doing other stuff...")
time.sleep(1)
# run_command()
# do_other_stuff()
# 두 개의 스레드를 생성
thread1 = threading.Thread(target=run_command)
thread2 = threading.Thread(target=do_other_stuff)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
'''
Starting command...
Doing other stuff...
Doing other stuff...
Doing other stuff...
Command finished.
'''
run_command에서 subprocess로부터 GIL이 해제가 된것인지 다음 실행으로 넘어가네요
즉, Thread에서 subprocess를 만나면 블로킹된다라는 것을 위 코드로 예측해볼 수 있는 것 같습니다
그러다 단순하게 생각해서 Thread에 subprocess가 아닌 CPU 바운드 작업만을 하면 어떨까?
당연히 GIL 에 의해 한 Thread를 독점하기에 맨 첫번째 코드와 똑같은 결과를 뱉어내겠지?
라는 생각으로 아래와 같은 코드로 실험해보았습니다
import threading
import subprocess
import time
def run_command():
print("Starting command...")
# subprocess.run(["sleep", "5"]) # 5초 동안 sleep하는 외부 명령
for _ in range(1000000000):
continue
print("Command finished.")
def do_other_stuff():
for i in range(3):
print("Doing other stuff...")
time.sleep(1)
# run_command()
# do_other_stuff()
# 두 개의 스레드를 생성
thread1 = threading.Thread(target=run_command)
thread2 = threading.Thread(target=do_other_stuff)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
'''
Starting command...
Doing other stuff...
Doing other stuff...
Doing other stuff...
Command finished.
'''
응? 분명 run_command에서는 CPU바운드 작업만으로 이뤄져 있기 때문에 해당 함수를 끝내고 난 후에
다음으로 넘어가야 하는게 정상이라고 생각했는데 do_other_stuff 도 동시에 실행되고 있는 상황이 발생해버렸습니다
이게 어떻게 된 것일까요?
거기에는 GIL에서의 컨텍스트 스위칭이라는 특징 때문에 그렇습니다.
여기서 저는 처음에 CS에서 통상적으로 말하는 컨텍스트 스위칭을 일컫는 말인가? 라고 생각했는데
알고보니 GIL의 컨텍스트 스위칭 개념과 일반적으로 말하는 컨텍스트 스위칭의 개념이 달랐습니다
일반적으로 컨텍스트 스위칭의 개념
여러개의 프로세스가 실행되고 있을 때 기존에 실행되던 프로세스를 중단하고
다른 프로세스를 실행하는 것. 즉, CPU에 실행할 프로세스를 교체하는 기술
GIL에서의 컨텍스트 스위칭의 개념
CPython에서의 GIL은 한 번에 하나의 스레드만 파이썬 바이트코드를 실행하도록 제한합니다.
그러나 GIL은 일정 시간 간격마다 해제되어 다른 스레드에게 실행 기회를 줍니다.
GIL의 특성 때문에 발생하는, 파이썬의 멀티 스레딩 환경에서만 나타나는 특별한 현상입니다
즉슨 GIL의 컨텍스트 스위칭은 Thread가 주기적으로 한번씩 풀려서 다른 Thread를 접근할 수 있게 해준다는 의미를
가지고 있고, 위 코드 현상은 GIL에서의 특별한 현상으로부터 발생할 수 있음을 알 수 있는 것이죠
'프로그래밍 언어 > Python' 카테고리의 다른 글
소켓코드를 보고 현상정리 ( feat. 버퍼 ) (1) | 2023.09.21 |
---|---|
with와 @contextlib.contextmanager (0) | 2023.09.20 |
Thread와 Queue를 활용한 파이프라인 구현하기( feat. 콘웨이 생명게임 ) (0) | 2023.09.19 |
파이썬에서 외부 터미널 명령어 사용하기 ( subprocess ) (0) | 2023.09.19 |
GIL를 무조건 신뢰하면 안되는 이유( feat.스레드 감수성 ) (0) | 2023.09.15 |