서론
문제점 : 두개의 run() 동시 실행 불가
파이썬 빌트인 라이브러리이며 많이 쓰이는 sched.scheduler 모듈은
몇초 뒤에 실행될 모듈을 enter 함수를 통해 입력하고
마지막에 run 을 통해 일괄적으로 수행한다 ( 수행 중에도 계속 enter 로 입력받을 수 있다.)
Flask 또한 마지막에 app.run 을 통해 서버를 구동한다.
나는 어떤 작업이 반복적으로 실행되면서, 서버를 구동시켜야 하는데,
이 두개의 run 은 일반적으로 동시실행 할 수 없다.
어느 한 부분에서 run 을 호출하면 그 라인에 멈춰서 그 부분만 실행하게 된다.
어떻게 해결할 수 있을까!?
(시행착오 스토리를 읽고 싶다면 클릭)
보통 스케줄링은 scheduling 라이브러리를 별도로 받아서 한다.
하지만 우리 회사는 철저한 망분리 보안정책으로 인해 개발망 컴퓨터는 오프라인 환경이었고,
별도의 라이브러리를 추가하려면 인터넷망에서 zip형태로 다운로드 받은 후
결재받고 또 옮기고 서버에 설치하고 전산망에 설치하는 작업은 더이상 Naver...
다음 알게된 라이브러리는 multiprocessing 인데, 어찌해도 이 것으론 실행할 수 없었다.
Process, Pool, Queue 등 배워야할 개념도 엄청나고,
뭔가 그런 그... 굳이 필요없는 고오급 기술이었다고나 할까...
sched 는 잘 쓰는 사람이 없어서 정보량도 적었다.
꼼수를 총동원해서 갖은 시도를 다 해봤다.
app.run 을 sched.scheduler enter 로 넣어보기도 시도하고,
Process (target = app.run) Process(target = s.run) 도 해보고,
Process(target = app.run) 만 넣고 s.run 은 그냥 실행도 해보고,
바꿔서도 해보고,
그냥 다 안됐다.
TypeError: can't pickle _thread.lock objects
요로코롬 화려한 에러가 날 감싸더라...
결정적으로 Process(target = app.run, args = ") 해서 Argument 를 넣어야하는데,
Argument 는 변수의 집합만 발지,
app.run(host = "0.0.0.0") 과 같이 (변수이름 = 변수) 와 같은 형식의 파라미터는 지원하지 않았다.
app.run 이 있는 run.py 를 쫓아들어가서 default 값 자체를 수정하기도 해봤는데 그것 역시 먹통.
한시간 가량의 삽질 결과, 해결책을 찾아낸다.
Threading 라이브러리
From threading import Thread
def init();
t1 = Thread( target = s.run )
t1.daemon = True
t1.start()
이 5줄로 그냥 끝났다.
여기서 daemon 속성은, 메인 쓰레드가 종료될 시 같이 종료되는 종속성을 가지느냐 여부이다.
이 것을 True 로 설정하지 않으면 메인쓰레드를 죽여도 서브쓰레드가 할 일이 있으면 계속 돌아간다.
개인적 평가:
장점: 간편함. 내장 라이브러리.
단점: Thread 자체를 출력해보면, 부여된 고유 id가 출력되는데, 같은 프로그램 내에서 같은 생성함수를 여러번 호출해도 매번 새로운 id가 부여된다. 고로 호출해야 하는 쓰레드가 많지 않을 때 사용할 것, 반복적으로 같은 기능을 하는 쓰레드를 호출(=생성)할 때 유의해야겠다.
Threading 와 Multiprocessing 의 차이:
Threading | 장 | 메모리를 공유해서 상태 공유가 쉽다. GIL① 을 이용한 병렬처리 가능 I/O bound② 애플리케이션에 옵션이 많다 |
단 | interrupt/kill 불가능 command queue③/message pump④ 모델을 따르지 않는 경우 동기화필요. race condition 이 발생할 수 있다. |
|
Multiprocessing (Threading 라이브러리를 기반으로 여러 프로세스의 작업을 나누는 기능을 제공한다.) |
장 | 메모리를 공유하지 않는다. 코드흐름이 명확하다. Multi core/CPU 의 장점을 쓸 수 있다. 자식 프로세스를 kill/interrupt 할 수 있다. 다양한 interface 를 사용할 수 있다. |
단 | 메모리를 많이 먹는다. IPC(Inter Process Communication)⑤ 에서 오버헤드가 더 크다. |
결론
기능구현을 위해 Threading을 선택했지만, 개발중인 시스템에게 더 적합한 라이브러리는 뭘까?
개발중인 환율시스템 자체가 몸집이 크지 않고, 여타 시스템에 붙을 서브시스템이기 때문에.
메모리를 적게 먹고 환율시스템 규모에 적당하게 심플한 Threading을쓰는 것이 더 어울리지 않을까 라는 생각을 한다.
만약 더 복잡하고 추후 기능들이 추가될 수 있는 시스템이었으면 multiprocessing 을 쓰는 것이 나을 것 같다.
주석:
- GIL : Global Interpreter Lock. 파이썬에서 쓰레드를 동기화 문제를 해결해주는 매커니즘이다. 여기서 말하는 인터프리터는 파이썬 인터프리터를 말하고, 락은 프로세스를 처리할 쓰레드가 하나씩 접근해 경합상태를 방지하기 위한 Lock 을 말한다. 파이썬 인터프리터는 근본적으로 한번에 한 쓰레드만 바이트코드를 실행하도록 제한해두었기 때문이다.
출처: 아주 잘 정리된 블로그 링크 - I/O bound : CPU 스케줄링에서 프로세스의 우선순위를 결정할 때 따지는 조건중 하나. I/O Bound process는 데이터의 인풋 아웃풋이 끝나기까지 기다려야하는 프로세스. 그의 반대개념인 CPU Bound process는 CPU를 많이 사용하는 프로세스를 말한다. I/O Bound가 데이터 인풋 아웃풋을 기다려서 처리시간이 오래걸리고, 컴퓨터의 성능도 어느정도 저하되지만, CPU사용시간이 짧기 때문에 일반적으로 I/O Bound 가 우선적으로 처리된다.
출처: 아주 잘 정리된 블로그 링크 - command queue : 이름 그대로 명령 대기열이다. 명령들을 대기열에 집어놓고 하나씩 나오게 하고, 이 제어를 통해 CPU를 다른 곳에 더 사용하는 등 목적을 달성한다.
출처: 위키피디아 - message pump : 한 스레드에게 긴 시간이 걸리는 작업(for / while 루프 등)을 그냥 실행시키면, 그동안 사용자의 입력을 받지 못하는데, message pump를 사용하면 작업 도중에 사용자가 입력을 할 수 있게 된다.
출처: 아주 잘 정리된 블로그 링크 - IPC(Inter Process Communication) : 프로세스 간 통신. 여러개의 프로세스가 돌아갈 때 타 프로세스의 주소공간을 침범(write)하면 안되기 때문에, 프로세스는 철저하게 분리되어 각자의 메모리, 각자의 주소를 가진다. 하지만 프로세스 간 데이터를 주고받으며 통신해야할 일이 분명 있는데 (센서로부터 읽은 값을 타 프로세스에게 전달하거나, LCD에 출력할 메시지를 전달하는 등), 이 것을 IPC라고 한다. 대표적으로 1. 시그널, 2. 파이프, 3. 메시지큐 등이 있다.
- 시그널 (리눅스 Ctrl+C 와 같이 신호를 보내는 것), 파이프 (cat a.txt | grep someword), 메시지 큐(자기 주소공간의 메시지를 메시지큐 주소공간으로 복사하고, 메시지큐 주소공간에서 대상 주소공간으로 복사하여 데이터를 전달) 출처: 아주 잘 정리된 블로그 링크
도움이 되셨다면 ♡공감 ↓광고클릭으로 저를 응원해주세요!!! :D
'개발 > Python & Flask' 카테고리의 다른 글
pymysql exception error (예외 처리 정리 및 해석!) (0) | 2020.11.10 |
---|---|
logging에 필터추가하기! 초간단 예시 (0) | 2020.11.10 |
pymysql 사용법 및 초간단 예시! (0) | 2020.11.10 |
Flask app.run() 파라미터 연구와 UnicodeDecodeError... (2) | 2020.11.10 |
Python Flask Logging 사용법!! - 서버관리 필수요소 (2) | 2020.11.10 |