본문 바로가기
Tech/IT기술 리뷰

파이썬 - 멀티프로세싱 종료처리 하기 (terminate가 안먹힐때)

by xproJason 2022. 2. 21.
반응형
파이썬에서 멀티프로세싱을 종료시켜보자

 

 

파이썬에서 멀티스레드를 종료하는 법에 대해 지난 글에서 알아보았다. 

https://xprojason.tistory.com/entry/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C%EB%A5%BC-%EB%94%B0%EB%A1%9C%EB%94%B0%EB%A1%9C-%EC%A2%85%EB%A3%8C%EC%8B%9C%EC%BC%9C%EB%B3%B4%EC%9E%90-%EC%9E%AC%EC%8B%9C%EC%9E%91%EC%8B%9C%EC%BC%9C%EB%B3%B4%EC%9E%90

 

파이썬 - 멀티스레드를 따로따로 종료시켜보자 (+ 재시작시켜보자)

파이썬에서 멀티스레드는 유용한 기능이다. 하나의 스레드에 각각의 하위 스레드와 데몬 스레드가 작동하게 만들고, 불필요하거나 재기동이 필요한 스레드는 종료시켰다가 다시 시작하게 할

xprojason.tistory.com

 

 

그렇다면, 멀티프로세싱은 어떻게 종료처리할까?

일반적으로는 멀티스레드보다 멀티프로세싱이 유용하다고 생각한다. 그 이유는 파이썬의 GIL 정책때문에 멀티스레드는 속도개선이 되지 않지만, 멀티프로세싱은 가능하기 때문이다. 물론 PC 성능이 뒷받침이 되어야 대용량 또는 빈번한 데이터 호출시 체감이 가능해 보이긴 한다. 

 

프로세싱을 종료처리하려면, terminate() 함수를 사용하라는 이야기를 많이 들었을 것이다.

하지만, 단일 프로세싱이 아닌, 멀티프로세싱에서 terminate() 함수를 날리면 아래와 같은 메시지가 발생한다.

attributeerror: 'nonetype' object has no attribute 'terminate'

멀티프로세싱의 타입을 인지하지 못해 에러가 발생하였다. 그래서 멀티프로세싱을 종료하려면 pipe를 사용하여야 한다.

 

지금까지의 결론은,

멀티프로세싱의 종료처리는, 멀티스레드보다 활용바운더리가 좁다 (현재까지 파악한 바로는 그렇다) 

아래 예제를 살펴보자.

 

스택오버플로우에서 발견한 예제이고, 해당 예제로 연습해 보았다.

멀티프로세싱 사용시는 pipe를 활용해 해당 프로세싱만 종료시켜주어야 한다. 그리고 signal.SIGTERM을 함께 날려주어야 하는데, 이는 '종료'를 뜻한다. 우리가 cmd에서 보통 ctrl+c 키를 사용하여 프로세스를 종료시킬 때, 이 signal.SIGTERM이 수행된다고 보면 된다. 

 

위 예제에서 def move와, def watch는 실행하고자 하는 각각의 프로세싱에 해당한다 (합치면 멀티프로세싱)

그리고, 각각의 def move와, def watch 하단을 보면 os.kill(~~pid, signal.SIGTERM) 이 보인다. 이 명령어가 멀티프로세싱을 종료하는 종료 명령어이다. 해석하면, os에 ~~pid를 찾고, SIGTERM 시그널을 보내어 종료시켜라는 뜻이다. 

이렇게 하면, def move은, motor.step 이라는 def가 모두 수행이되고나면 종료처리가 될것이다. (def watch 는 for문을 돌다가 해당 케이스가 있으면 종료처리가 된다) 

 

 

그렇다면 종료처리를 위한 사전 셋팅은 어떻게 해야 하는가?

 

 

reader 와 writer라는 두개의 변수를 Pipe 형태로 생성한다. 

그리고 Pipe를 통해 프로세스정보를 공유하게 된다. send에서 start 수행한 프로세스의 디스크립터 주소를 받아서 pipe를 통해 공유한다.

 

그리고, argument인 move_process_reader를 pipe로 사용하여, pipe에서 pid 정보를 수신한다.

recv는 pipe에서 pid 디스크립터 주소를 받는 역할을 담당하게 된다. 

 

이후 과정은, motor.step 의 로직이 모두 수행되고 나면, os.kill 을 사용해 해당 프로세싱을 종료시켜준다. 

 

여기까지가 terminate 가 먹히지 않을 때 (attributeerror: 'nonetype' object has no attribute 'terminate' 발생할때), 멀티프로세싱을 종료시키는 방법이다. 멀티스레드에비해 난이도가 있다고 느껴진 부분은 pipe의 사용과 send - recv로 이어지는 구조이다. 특이한 점은, pipe를 통해 연결할때 두개의 변수인 move_process_reader 와 move_process_writer를 사용하는데, 이 둘이 pipe를 통해 연결된다는 점이다.  하나의 파이프를 통해 변수를 생성하면 두개가 연결되는 것으로 보인다. 

 

단점은, 멀티스레드에 비해 종료시키는 구조에 한계가 보인다는 점이다. 멀티스레드는 while 문을 통해 반복되는 로직을 종료시키는 방법이 다양했는데, 멀티프로세싱은 같은 def 내부에 실행로직이 있어야 종료가 가능하다. (현재까지 파악한 바로는 그렇다. 반박시 님말이 옮음)

 

개인적으로는 GIL 정책에 의해 속도차이가 현저하게 느껴지는 구조가 아니고, 다양한 시점에 종료가 필요하다면 멀티스레드를 추천하는 바이다.

 

 

글쓴이 : xpro.jason@gmail.com

[무단배포는 노노, 링크배포는 Okay]

 

- 글작가 Jason 의 병맛지식 -

 

 

반응형

댓글