Programming/Python

[Python] 멀티 프로세스와 멀티 스레드 (Multi process, multi thread and context switching)

sahayana 2022. 3. 6. 22:41


#1.  프로세스와 스레드

파이썬의 비동기 함수 async/await을 이해하는 과정에서 문득 떠오른 의문을 튜터님께 여쭤본 적이 있습니다.

"비동기 함수를 통한 병렬 수행으로 동시 처리와 성능 향상이 되는 것은 알겠는데... 그럼 모든 함수에 async/await을 쓰면 되는 건가요?"

답은 "꼭 그렇지는 않다" 였습니다.

 

"백엔드 개발자라면 시스템 설계 검증 과정을 통해 주어진 요구사항에는 어떠한 방법
(멀티프로세스/멀티스레드/비동기함수)이 가장 효율적인지 찾는 것이 중요하다. "

 

무엇인가 꽤나 중요한 것을 관통하는 메시지같은데, 사실 무슨 의미인지 잘 몰라서 한번 알아봤습니다.

 

 

[프로세스(Process)]

  • 프로그램이 메모리 상에서 구동되는 일종의 작업 단위
  • 운영체제로부터 프로세서, 주소 공간, 메모리 등의 자원을 할당받음
  • 프로세스 하나가 죽어도 다른 프로세스에는 영향이 없음
  • Context switching 느림

 

[스레드(Thread)]

  • 프로세스안에서 실행되는 흐름의 단위(실제 작업의 주체)
  • 프로세스가 할당받은 자원을 이용
  • 하나의 프로세스 안에서 작업하기 때문에 자원 공유가 쉬움
  • Context switching 빠름

 

[Context switching]

  • CPU가 실행하는 작업을 변경
  • CPU가 현재 작업중인 task를 저장하고, 이후 scheduled된 task를 실행하는 것
  • 파이썬 비동기함수에서 await 함수를 만나면 context switching이 일어남

#2. 멀티  프로세스와  멀티 스레드

https://velog.io/@sehrltjr/Thread

[멀티 프로세스(Multi Process)]

  • 하나이상의 작업에 여러개의 프로세서(CPU)를 사용하여 병렬 처리
  • 독립된 프로세스로 한 프로세서의 문제가 다른 프로세스에 영향을 주지 않음
  • 안정성이 높음
  • 캐시 메모리 초기화 등으로 context switching 느림

[멀티 스레드(Multi Thread)]

  • 한 프로세스에서 여러 스레드로 자원을 공유하면서 작업을 나눠 수행
  • 스레드끼리 데이터 교환이 쉽고 자원 소모가 적어 시스템 처리율 향상
  • 캐시 메모리 초기화를 하지 않아 context switching 빠름
  • 자원 공유로 동기화 문제 발생 가능성
  • 어려운 디버깅
  • 스레드 하나에 문제가 생겨도 프로세스에 영향

#3. 파이썬과 멀티 스레드

 

파이썬은 GIL(Global Interpreter Lock)이라는 제약 때문에 인터프리터 실행에 있어 하나의 스레드만 사용가능한
싱글스레드 언어 입니다.

 

"The Python Global Interpreter Lock or GIL, in simple words, is a mutex (or a lock) that allows only one thread to hold the control of the Python interpreter."

 

즉, 하나의 스레드에 모든 자원을 할당하고 다른 스레드에는 lock을 걸어 실행불가능하도록 만듭니다.
왜 그럴까요?


#4. Why GIL?

 

파이썬의 모든 객체는 reference count 즉, 참조 횟수를 메모리 주소에 할당하고 이를 통해 메모리를 관리합니다.

>>> import sys
>>> a = []	# count = 1
>>> b = a	# count = 2
>>> sys.getrefcount(a)	# count = 3
3

문제는 다수의 스레드에서 이 객체를 '동시에' 수정한다면, 이 객체가 존재하는 한 지속적으로 엉망진창인 value를 반환합니다. 이는 곧 충돌과 버그를 유발시킵니다.

따라서 모든 객체에 lock을 걸어버림으로써 객체가 가진 고유한 reference count를 안전하게 보관합니다.
(safe memory management)

 

Single-threaded 프로그램에서는 이러한 GIL에 의한 병목현상은 눈에 띄지 않지만, 연산 작업이 많이 필요한
CPU bound 작업에서는 연산 속도에 조금 차이가 있다고는 합니다.

 

또한 싱글 스레드가 멀티 스레드보다 무조건 느린 것도 아닙니다.

같은 작업이라도  스레드간 context switching으로 인해 멀티 스레드의 작업 시간이 더 오래 걸릴 수 도있습니다.


#5. 파이썬의 Thread 사용

 

파이썬도 threading 내장 모듈을 통한 스레드 스케쥴링으로 멀티 스레드를 사용할 수 있습니다. 

다만, GIL때문에 다중 CPU에서 각각의 코어에 할당하지 않습니다.

 

그럼 파이썬의 Thread 병렬처리는 한 코어만 박살내고 다른 코어는 그냥 냅두냐? 그건 또 아닌 것 같습니다.
관련하여 파이썬 Theading 모듈을 사용했을 때 CPU 사용량을 소개한 멋진 블로그가 있는데 이곳을 참조해주세요.


#6. 정리

 

파이썬의 GIL을 파이썬이 가진 한계라고 인식하는 경우도 많다고 하고, 이 때문에 파이썬을 사용하는 장고와 같은 웹프레임워크들은 다중 스레드처리가 힘들어 사용할 경우가 제한적이라는 인식도 있다는데 비슷한 Node.JS도 그렇고 전세계적인 장고 점유율을 봤을 때 사실 그리 걱정할 문제는 아닌 것 같습니다.

  • 파이썬은 싱글 스레드 언어다.
  • 멀티코어 자원을 잘 활용하려면 Multiprocess로 효율성을 따져 보자. (파이썬 공식문서)
  • I/O bound의 병목을 해결하기 위한 Threading 모듈 사용을 효과적일 수 있다.
  • CPU bound에는 약한 모습을 보인다.
  • 파이썬의 GIL은 호불호가 있다. (Safety memory management / Objects lock)

#7. 참고