행복한 하루

Raspberry Pi에서 쓰레드(Thread)와 메시지 큐(Message Queue) 통신 이용해 보기(Python) 본문

RaspberryPi

Raspberry Pi에서 쓰레드(Thread)와 메시지 큐(Message Queue) 통신 이용해 보기(Python)

변화의 물결 2022. 5. 22. 17:05

 

 

안녕하세요.

 

라즈베리 파이 프로그램에서 쓰레드를 2개 이상 사용할 경우 쓰레드 간에 데이터를 주고받아야 하는 경우가 있습니다. 혹은 인터럽트 처리를 중 값을 전달해야 할 경우도 있습니다.

 

 단순한 장난감 자동차를 예를 들어 개발한 한다고 했을 경우, 핸들 조작을 감지하는 쓰레드와, 앞뒤를 감지하는 센서 인터럽트 쓰레드가 필요하고, 이 조작에 따라 모터가 동작하는 쓰레드가 필요하다고 가정합니다.

이럴 경우 쓰레드 간에 메시지를 주고받아야 하는 경우에 메시지 큐를 통해 주고받을 수 있습니다.

 대략적은 그림으로 표현하자면 다음과 같습니다.


1. 소스 확인

 - Queue, Thread 호출에 필요한 라이브러리를 import 합니다.

 - Queue 크기를 설정해서 메모리에 초기화합니다.

 - Queue에 데이터를 넣는 Thread 2개(handle, sensor)와 Queue에서 값을 출력하는 Thread 1개(Motor)로 구현합니다.

 

import queue
import threading 
import time

flag_exit = False

QUEUE_SIZE = 10
mq = queue.Queue(QUEUE_SIZE)

def handle_main() :
	while True:
		#print("\tHandle")
		mq.put("Handle");
		time.sleep(0.5)

		global flag_exit
		if (flag_exit): 
			break

def sensor_main() :
	while True :
		#print("\tSensor")
		mq.put("Sensor");
		time.sleep(1)

		global flag_exit
		if (flag_exit) :
			break

def motor_main() :
	while True :
		try :
			data = mq.get(True,2)
			print("Data " + data)
		
			global flag_exit
			if (flag_exit) :
				break
		except queue.Empty:
			print("Stop")
			break


def main():
	print("Thread & Message Queue Example")
	try:
		t1 = threading.Thread(target=handle_main)
		t1.start()
		t2 = threading.Thread(target=sensor_main)
		t2.start()
		t3 = threading.Thread(target=motor_main)
		t3.start()
		while True:
			time.sleep(0.1)
	
	except KeyboardInterrupt:
		print("Ctrl+C Pressed.")
		global flag_exit
		flag_exit = True
		
		t1.join()
		t2.join()
		t3.join()
		
if __name__ == "__main__":
	main()

  

   - Queue와 Thread 동시에 사용하다 보니, Ctrl+C 사용해서 종료할 때 에러 메시지들이 나왔습니다. 그래서 조금 편법으로 에러를 나오지 않게 구현해보았습니다. 정확한 구현이 아니므로 필요하다면 Python에 대해 이해 후 수정해서 사용해야 할 것으로 보입니다.

 

  - Thread에서 변수를 공유하기 위해서 global 예약어 사용해서 flag_exit 선언했습니다. (추가로, 여기서는 읽기만 하기 때문에 Lock을 사용하지 않았지만, 변수에 쓰기(Write)를 한다면 Lock을 걸어주어야 합니다. lock.acquire(), lock.release()를 검색해서 찾아보시면 됩니다.)

 

   - queue.get() 경우 기본값이 block=True 이므로 queue 안에 값이 없으면 대기상태가 됩니다. 그래서 중간에 중단하려면 멈춘 것처럼 대기 상태가 됩니다. 이 상황을 막기 위해서 몇 가지 방법이 있지만, 그중에 선택한 것은, block 상태에서 값이 없으면 2초 후에 빠져나오게 하였습니다.

 

  - queue 값이 없는 상태에서 중간에 빠져나오면이 없다고 에러가 발생했는데 그것을 처리하기 위해서 except queue.Empty로 예외 처리를 하였습니다.

2. 실행 결과

  - 조작하는 Thread와 출력하는 Thread가 Queue를 공유하며 동작하는 하는 것을 확인할 수 있습니다.

  - 위의 Python 코드는 완벽하게 마무리된 것이 아니기 때문에 다른 에러 상황도 발생할 수 있으니,  참고하여 보완해야 할 수 있습니다.

 

감사합니다.

 

 

<참고사이트>

1. 동기화된 큐 클래스

https://docs.python.org/ko/3.7/library/queue.html

2. 파이썬(Python) Thread – 동기화 설명

https://niceman.tistory.com/139

3. Why can I not catch a Queue.Empty exception from a multiprocessing Queue?

https://stackoverflow.com/questions/13941562/why-can-i-not-catch-a-queue-empty-exception-from-a-multiprocessing-queue

thread_queue.zip
0.00MB

 

 

Comments