멀티 스레드과 멀티 프로세스 개념을 공부하다가 파이썬으로 직접 테스트해보면 좋을 것 같아 공부해보았다.
멀티 스레드과 멀티 프로세스의 차이 요약
- 동시에 엑셀 파일을 여러 개 연다 = 멀티 프로세스
- 한 엑셀 파일에서 데이터 정렬, 필터링 작업, 함수 계산 등을 동시에 처리한다 = 멀티 스레드
- 멀티 프로세스 환경에서는 각 프로세스는 다른 프로세스의 작업에 영향을 받지 않음 (내가 a라는 엑셀 파일에 작업하는데 b파일의 내용이 수정되거나 하지 않으니…)
- 멀티 스레드 환경에서는 유저가 동시에 여러 작업을 수행할 수 있으나, 이 작업 간 동기화가 주요 이슈가 된다.
- 멀티 프로세싱 환경에서는 한 프로세스가 깨져도 다른 프로세스에 영향 없음
멀티 스레드 파이썬으로 실험하기
import threading
import time
def print_numbers():
for i in range(10):
time.sleep(1) # 1초 마다 숫자를 출력한다
print(i)
def print_letters():
for letter in '가나다라마바사아자차':
time.sleep(1) # 1초 마다 문자를 출력한다
print(letter)
lock = threading.Lock()
# 두 개의 스레드를 만든다
t1 = threading.Thread(target=print_numbers) # print_numbers를 호출하는 스레드
t2 = threading.Thread(target=print_letters) # print_letters를 호출하는 스레드
# 스레드를 시작시킨다
t1.start()
t2.start()
# 두 스레드가 끝날 때까지 기다린다
t1.join()
t2.join()
print("테스트 종료!")
출력 결과
두 스레드가 이렇게 동시적으로 출력 작업을 실시한다.
호출할 때마다 결과가 동일하지 않다 → 결과 예측이 안 된다
멀티 프로세스 파이썬으로 실험하기
import multiprocessing
import time
lock = multiprocessing.Lock()
def print_numbers():
for i in range(10):
time.sleep(1) # 1초 마다 숫자를 출력한다
print(i)
def print_letters():
for letter in '가나다라마바사아자차':
time.sleep(1) # 1초 마다 문자를 출력한다
print(letter)
# 프로세스를 생성하는 작업은 메인 코드 블럭에 넣어야 한다
if __name__ == '__main__':
# 두 개의 프로세스를 만든다
# print_numbers를 호출하는 프로세스
p1 = multiprocessing.Process(target=print_numbers)
# print_letters를 호출하는 프로세스
p2 = multiprocessing.Process(target=print_letters)
# 프로세스를 시작시킨다
p1.start()
p2.start()
# 두 프로세스가 끝날 때까지 기다린다
p1.join()
p2.join()
print("테스트 종료!")
- 프로세스 개수가 많아지면 리스트에 추가하고, for문을 이용해
join()
메소드를 호출시킬 수 있다.
유의할 점
출력에 관해 얘기하기 전에 멀티 스레드 예제와 거의 비슷한데 살짝 다른 점이 있다! 바로 아래 부분이다.
# 프로세스를 생성하는 작업은 메인 코드 블럭에 넣어야 한다
if __name__ == '__main__':
이 지시를 따르지 않으면 아래와 같은 RuntimeError가 뜨면서 코드가 제대로 실행되지 않는다.
raise RuntimeError('''
RuntimeError:
An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.
This probably means that you are not using fork to start your
child processes and you have forgotten to use the proper idiom
in the main module:
if __name__ == '__main__':
freeze_support()
...
The "freeze_support()" line can be omitted if the program
is not going to be frozen to produce an executable.
대략 요약하자면 자식 프로세스를 만들기 위해 fork를 사용하지 않고 있으므로, 메인 코드 블럭(if __name__ == '__main__':
) 을 사용하라는 이야기다. 이는 예상치 못한 에러라 의아했는데 다음과 같은 숨겨진(?) 사연이 있었다.
윈도우 운영체제는 리눅스 같은 유닉스 계열 운영체제와 달리 새로운 프로세스를 만들 때 fork
를 사용하지 않는다. 윈도우의 프로세스 생성 메커니즘은 spawn
이라고 한다.
spawn
을 사용하면 새로운 프로세스가 생성되고 해당 프로세스에서 파이썬의 메인 모듈(그러니까 내가 실행한 저 코드, 파이썬 인터프리터로 직접 실행된 스크립트 파일)를 다시 로드한다. 이 과정에서 메인 모듈 안의 모든 코드가 새 프로세스에서 다시 실행되면서, 예상치 못한 에러가 발생하는 것이다.
[!INFO] 메인 모듈이란? 메인 모듈이 아닌 경우는?
예를 들어, 쉘에서python my_script.py
와 같은 파일을 실행했다고 치면, 이때my_script.py
파일이 메인 모듈이다.메인 모듈이 아닌 경우는 다른 모듈에서
import
를 통해 불러오는 경우가 있다.
예를 들어,my_script.py
파일에서import another_module
이라고 적으면, 이때 이another_module.py
는 메인 모듈이 아닌 다른 모듈로서 로드되는 것이다.
if __name__ == '__main__':
구문을 사용하면 메인 모듈로 실행될 때만 블럭 안의 코드를 실행할 수 있게 한다.
이러한 부작용을 방지하기 위해 if __name__ == '__main__':
블록을 사용한다.
이 조건문의 의미는 __name__
변수가 __main__
일때만 다음 코드를 실행하라는 뜻이다. (파이썬의 스크립트가 직접 실행되면 __name__
변수에 "__main__"
이 할당되고, 만약 모듈이 임포트되면 해당 모듈의 이름이 할당된다.)
따라서 이 블록 안의 코드는 스크립트가 메인 모듈로 실행될 때만 실행된다. 새 프로세스에서 메인 모듈이 다시 로드되거나, 혹은 이 스크립트가 다른 파일로 임포트되어 실행될 때와 같은 경우에는 해당 코드가 실행되지 않는다. 원치 않는 코드 실행을 방지할 수 있고 부작용을 막을 수 있다.
출력 결과
스레드의 경우보다 규칙적으로 p1 -> p2 순으로 번갈아가며 출력 작업을 실시한다.
하지만 중요한 것은 완벽하진 않다! (프로세스의 실행 순서 또한 운영체제의 스케줄러에 의존하고 있기 때문에) 그치만 대부분의 경우 저렇게 출력되며, 멀티 스레드보다는 비교적 예측 가능한 결과를 보였다. 신기방기.
'운영체제' 카테고리의 다른 글
운영체제 개념 노트 (0) | 2022.12.16 |
---|