最近的物聯網智能網關(樹莓派)項目中遇到這樣一個問題:要從多個底層串口讀取發來的數據,並且做出相應的處理,對於每個串口的數據的讀取我能想到的可以采用兩種方式:
一種是采用輪詢串口的方式,例如每3s向每個串口的buffer區去取一次數據,但是這樣可能會有緩沖區溢出的可能,同時,數據的同步也可能會出現一定的問題,因為數據的上傳周期是可以用戶自定義的,一旦用戶定義的上傳周期過短或過長,都可能造成讀取的數據出問題。
另一種方式,就是采用多線程方式,把每個串口讀取數據放在單獨的子線程中,每個子線程阻塞於串口讀,后來在測試時發現,由於python的threading模塊沒有實現對信號的處理,所以當父線程阻塞時,我們需要安裝一個信號處理函數,例如,抓取一個Ctrl+C的SIGINT信號的抓取,以方便我們測試程序的退出。下面記錄下遇到的坑:
1、在多線程中,首先要把所有的子線程在start之前設置為daemon;
2、如果父線程調用.join()方法實現對自身的阻塞,那么父線程將永遠都抓不到目標信號,因此應該要使用isAlive()方法模擬父線程阻塞,然后不斷輪詢子線程的運行狀況;
3、完成對於sig_handler()函數的處理。
實現代碼如下:
def blue_thread(): ser_blue = serial.Serial("/dev/ttyUSB0", 9600) # 藍牙串口 print '\033[31;1m=======藍牙子線程啟動=======\033[0m' while ser_blue.isOpen(): count = ser_blue.inWaiting() if count != 0: recv = ser_blue.read(count) print recv # str_handle(recv) time.sleep(1) ser_blue.close() def zigbee_thread(): ser_zigbee = serial.Serial("/dev/ttyS0", 115200) # zigbee串口 print '\033[32;1m=======Zigbee子線程啟動=======\033[0m' while ser_zigbee.isOpen(): count = ser_zigbee.inWaiting() if count != 0: recv = ser_zigbee.read(count) print recv # str_handle(recv) time.sleep(1) ser_zigbee.close() def signal_handler(num,frame): print '\033[33;1m===BYEBYE====\033[0m' sys.exit(0) def main(): # 由於python的多線程模塊沒有實現對信號的處理,所以父線程阻塞,我們需要安裝一個信號處理函數,能讓我們方便退出程序,並且要把子線程設為daemon signal.signal(signal.SIGINT,signal_handler) threads = [] threads.append(threading.Thread(target=blue_thread,args=())) threads.append(threading.Thread(target=zigbee_thread,args=())) for t in threads: t.setDaemon(True) t.start() # t.join() # 不能使用join,要使用isAlive方法模擬父線程阻塞,不斷輪詢子線程的運行狀況 while True: alive = False for t in threads: alive = alive or t.isAlive() if alive == True: break if not alive: break if __name__ == "__main__": main()
以上處理方法靈感源自:http://www.jb51.net/article/35165.htm