2017-06-22 62 views
1

我有以下代碼,我需要一次讀取多個傳感器。我已經設置了線程和多處理來爲我完成這個任務。當線程和多線程代碼位於主類之外時,它可以正常工作,但類不能使用它所檢索的數據。當我將該多線程代碼放入該類時,我遇到了一個EOFError: Ran out of input錯誤。EOFError:在輸入類內輸出

下面是代碼:

import os 
import multiprocessing 
from multiprocessing import Process, Pool 
import threading 
import queue 
import tkinter as tk 
from tkinter import * 
from tkinter import ttk 
import time 
import minimalmodbus 
import serial 
minimalmodbus.CLOSE_PORT_AFTER_EACH_CALL = True 
THREAD_LOCK = threading.Lock() 

class Application(tk.Frame): 
    def __init__(self, master=None): 
     super().__init__(master) 

     self.pack() 

     self.first_gas_labelframe = LabelFrame(self, text="Gas 1", width=100) 
     self.first_gas_labelframe.grid(row=0, column=0) 

     self.value_label = Label(self.first_gas_labelframe, text="Value") 
     self.value_label.grid(row=0, column=0) 

     self.unit_label = Label(self.first_gas_labelframe, text="Unit") 
     self.unit_label.grid(row=1, column=0) 

     self.temp_label = Label(self.first_gas_labelframe, text="Temp") 
     self.temp_label.grid(row=2, column=0) 

     self.temp_label6 = Label(self.first_gas_labelframe6, text="Temp") 
     self.temp_label6.grid(row=2, column=0) 

     self.timer_button = tk.Button(self, text='Start', command=self.start_run) 
     self.timer_button.grid(row=2, column=0) 


    def start_run(self): 
     self.all_thread() 

    def all_thread(self): 
     thread = threading.Thread(target=self.all_process) 
     thread.start() 

    def all_process(self): 
     all_ports = port_num() 
     gas = minimalmodbus.Instrument("COM3", 1) 
     gas.serial.baudrate = 9600 
     gas.serial.bytesize = 8 
     gas.serial.parity = serial.PARITY_NONE 
     gas.serial.stopbits = 1 
     gas.serial.timeout = 0.25 
     gas.mode = minimalmodbus.MODE_RTU 

     gas_list = [gas] 
     processes = [] 
     while len(gas_list) > 0: 
      val = 1 
      with THREAD_LOCK: 
       for sen in gas_list: 
        proc = Process(target=self.main_reader, args=(sen, val)) 
        processes.append(proc) 
        proc.start() 
        val += 1 
       for sen in processes: 
        sen.join() 
       time.sleep(1) 

    def main_reader(sen, val): 
     try: 
      read = sen.read_registers(0,42) 
     except OSError: 
      read = "Communication Error" 
     except ValueError: 
      read = "RTU Error" 
     print(read) 

if __name__ == '__main__': 
    root = tk.Tk() 
    root.geometry("1000x600") 
    app = Application() 
    app.mainloop() 

有了一些調試,問題發生在proc.start()proc有數據。這些清單也有數據,這就是爲什麼我很困惑它爲什麼沒有投入。注:在我的代碼有六個項目在gas_list

回答

3

不能使用多處理這樣的(當然,你可以,但結果將是不可預知的) - 當你創建一個新的進程你從列表minimalmodbus.Instrument對象沒有按」作爲參考而不是通過,但作爲一個整體的新對象。 Python本質上運行一個全新的Python解釋器實例,只要你實現了一個實例,並且由於不同的進程獲得不同的堆棧,他們不會共享內部存儲器,所以Python實際上醃製了傳遞的參數,將它們發送到Process,然後在那裏取消它們造成一種錯覺,即兩個過程(父母和孩子)具有相同的數據。

你可以自己觀察,如果不是創建一個新的multiprocessing.Process而是你打電話self.main_reader(pickle.loads(pickle.dumps(sen)), val)val也會被醃製,但作爲一個通用它在這裏沒有任何重要性)。

非常相同的過程發生在Application.main_reader()方法(雖然古怪的定義),太 - 你有它設置方式是,你的整個Application實例實際上得到的子進程重新使Python可以調用它的main_reader()方法。

你可以做的是傳遞需要的參數來重新創建你的原始對象到子過程函數,然後在你的函數啓動時創建你的對象。例如,如果您修改Application.all_process()方法:

def all_process(self): 
    gas = {"__init__": ("COM3", 1) 
      "serial": { 
       "baudrate": 9600, 
       "bytesize": 8, 
       "parity": serial.PARITY_NONE, 
       "stopbits": 1, 
       "timeout": 0.25 
      }, 
      "mode": minimalmodbus.MODE_RTU} 

    gas_list = [gas] 
    processes = [] 
    while len(gas_list) > 0: 
     val = 1 
     for sen in gas_list: 
      # we'll be calling the main_reader function outside of the Application instead 
      proc = multiprocessing.Process(target=main_reader, args=(sen, val)) 
      processes.append(proc) 
      proc.start() 
      val += 1 
     for sen in processes: 
      sen.join() 
     time.sleep(1) 
     # you do plan to exit this loop, right? 

,然後讓Application類以外定義爲您的main_reader()功能:

def main_reader(data, val): # notice it's outside of the Application scope 
    sen = minimalmodbus.Instrument(*data["__init__"]) # initialize Instrument 
    for k, v in data["serial"].items(): # apply the serial settings 
     setattr(sen.serial, k, v) 
    sen.mode = data["mode"] # set the mode 
    try: 
     read = sen.read_registers(0, 42) 
    except OSError: 
     read = "Communication Error" 
    except ValueError: 
     read = "RTU Error" 
    print(read) 

應該停止引發錯誤。此外,您在原始代碼中使用了threading.Lock - 我不知道您在嘗試使用它做什麼,但它絕對不會做您認爲它的作用。

+0

很好的答案!看起來我還有很多東西需要學習。無論如何,我可以在'Application'類中保留'main_reader',所以如果我需要更改Tkinter中的標籤,我可以在同一個類中引用它? – GreenSaber

+0

@Evan - 有一種方法 - 有點。檢查[這個答案](https://stackoverflow.com/a/44186168/7553525),但它仍然創建一個新的實例,只是在一個更可控的方式。如果您只需要「共享」標籤(字符串),最好在全局名稱空間中創建一個獨立的結構/類,以保存標籤,然後從「Application」類和獨立函數中引用它們。只要確保你沒有在'__name__ ==「__main __」'guard後面定義它。 – zwer

+0

我需要在過程之後更改每個標籤,這將是每秒一次。我只需要一種方法將'read'讀入'Application()'中。 – GreenSaber