들어오는 두 데이터 스트림을 처리하고 파이썬으로 결합합니까?
나는 두 개의 들어오는 스트림을 처리하고 결합하는 방법으로 스레딩, 다중 처리 비동기 등의 파이썬에서 다양한 옵션을 연구하고 있습니다. 많은 정보가 있지만 예제는 종종 복잡하고 복잡하며 일반적으로 단일 작업을 여러 스레드 또는 프로세스로 분할하여 작업의 최종 결과를 가속화하는 것입니다.
소켓을 통해 들어오는 데이터 스트림 (현재 내 PC에서 로컬로 실행되는 다른 응용 프로그램으로 UDP를 사용하고 있지만 응용 프로그램을 별도의 PC에서 실행해야하는 경우 나중에 TCP로 전환하는 것을 고려할 수 있음) 및 직렬 스트림이 있습니다. RS232 어댑터를 통해 들어오고 스트림을 결합해야합니다. 이 새 스트림은 다른 소켓에서 재전송됩니다.
문제는 서로 다른 속도로 들어오는 것입니다 (직렬 데이터는 125hz, 소켓 데이터는 60-120hz). 따라서 소켓 데이터에 최신 직렬 데이터를 추가하고 싶습니다.
내 질문은 본질적으로 다른 사람들의 이전 경험을 바탕으로 이것을 처리하는 가장 좋은 방법입니다. 이것은 본질적으로 I / O 작업이기 때문에 스레딩 (내가 알고있는 GIL에 의한 동시성에 제한됨)에 더 적합하지만 높은 입력 속도로 인해 다중 처리가 갈 길인지 궁금합니다.
스레딩을 사용하는 경우 각 공유 리소스에 액세스하는 가장 좋은 방법은 잠금을 사용하여 직렬 데이터를 객체에 쓰고 새 소켓 데이터가있을 때마다 별도의 스레드에서 잠금을 획득하여 최신 직렬 데이터에 액세스하는 것입니다. 객체를 처리하고 다른 소켓으로 보냅니다. 그러나 메인 스레드는 각각의 새로운 수신 소켓 메시지 사이에 많은 작업을합니다.
다중 처리를 사용하면 파이프를 사용하여 다른 프로세스에서 최신 직렬 데이터를 요청하고 수신 할 수 있지만 직렬 데이터 처리를 오프로드 할 뿐이며 여전히 주 프로세스에 많은 것을 남깁니다.
답변
여기에 멀티 스레딩 이 필요 합니까? 엄격하게 필요하지 않다면 나는 그것을 피할 것입니다.
- 나는 최근에 직렬 포트와 소켓에 대해 너무 많이 프로그래밍하지 않았지만 내가 아는 한, 두 데이터가 모두 HW / 미들웨어에 의해 버퍼링되기 때문에 들어오는 스트림 당 스레드가 필요하지 않습니다.
- 해야 할 일이 많은 메인 스레드와 관련하여 : I / O를 수행하는 스레드에서 이것이 결합 될 수 없다고 확신합니까?
어떻게 든 가능하다면 두 스트림에서 교대로 읽는 루프를 작성하고 처리 / 결합하여 출력 소켓에 씁니다.
while True:
serial_data_in = serial_in.read()
socket_data_in = socket_in.read()
socket_out.write(combine(serial_data_in, socket_data_in))
다른 하나에서 들어오는 데이터가 없을 경우 하나의 데이터 누락을 방지하기 위해 read ()의 시간 초과에 대한 tweeking이 필요할 수 있습니다.
작동하지 않으면 가능한 한 적은 수의 스레드를 유지합니다. 예를 들어 읽기에 하나의 스레드를 사용하고 (위와 같이) 큐 를 사용 하여 처리를 수행하고 출력 소켓에 쓰는 스레드와 통신 할 수 있습니다.
q = queue.Queue()
def worker_1:
while True:
serial_data_in = serial_in.read()
socket_data_in = socket_in.read()
q.put((serial_data_in, socket_data_in))
def worker_2:
while True:
(serial_data_in, socket_data_in) = q.get()
socket_out.write(combine(serial_data_in, socket_data_in))
q.task_done()
큐 는 잠금 개체의 낮은 수준의 동기화 복잡성을 제거합니다.
저는 select를 사용 하는 것이 매우 간단 하다고 생각 합니다. EOF
읽을 데이터 (또는 ) 가있는 소켓을 알려줍니다 .
사실, 이전에 비슷한 질문이있었습니다. Python-두 개의 UDP 소켓에서 수신하는 서버
에서 반환 된 소켓에서 하나의 읽기만 select
차단되지 않도록 보장합니다. 계속 읽기 전에 다시 확인하십시오. 즉, 데이터 스트림을 읽는 경우 전체 라인 또는 처리 할 수있는 다른 데이터 단위를받을 때까지 버퍼로 읽어야합니다.
네트워크 및 직렬 인터페이스에서 읽어야하기 때문에 질문이 연결된 질문과 다릅니다. Linux에는 문제가 없으며 모든 파일 설명자를 select
. 그러나 Windows에서는 소켓 만 select
. 나는 Windows에서 작동하지 않지만 직렬 회선을 읽는 데 전용 스레드가 필요한 것 같습니다.
여기에 사용 된 접근 방식을 제안 할 수 있습니다. https://stackoverflow.com/a/641488/4895189. 소켓과 시리얼을 통해받은 데이터에 대한 구조가 있다면 타임 스탬프가있는 구조를 개별 파이프 객체에 쓸 수 있습니다.
내 경험에서 스레딩보다 다중 처리를 선호합니다. UART의 읽기 및 쓰기에 pyserial을 사용했습니다. 여기서 주 스레드는 쓰기에 사용되고 별도의 스레드는 읽기에 사용되었습니다. 알 수 없었던 이유로 순차 쓰기 호출 사이에 상당히 큰 지연 (~ 1000ms)을 추가하지 않고 데이터를 작성하면 입력과 출력 모두에서 프레임을 놓쳤습니다. 일반적으로 Python의 Threading과 함께 pyserial을 사용하면 이상한 동작이 나타납니다. 현재로서는 pyserial의 구현 또는 Python의 GIL 때문인지 확실하지 않습니다.
즉, 위에서 링크 한 답변을 기반으로 설정에 다음 구조를 사용할 수 있다고 생각합니다.
자식 프로세스 1-소켓에서 데이터를 읽고 타임 스탬프를 사용하여 파이프에 쓰기
자식 프로세스 2-pyserial을 사용하여 데이터를 읽고 타임 스탬프를 사용하여 파이프에 쓰기
주 프로세스-선택한 간격으로 두 파이프 개체에서 선택을 수행하고 스트림을 결합하고 출력 소켓으로 전송합니다.