EOFError:类内的输入用完
问题描述
我有以下代码,我需要一次读取多个传感器.我已经设置了线程和多处理来为我完成这项任务.当线程和多处理代码在主类之外时,它可以正常工作,但该类不能使用它检索的数据.当我将多线程代码放入类中时,我遇到了 EOFError: Ran out of input
错误.
I have the following code where I need to read multiple sensors at a time. I have set up threading and multiprocessing to do this task for me. When the threading and mutliprocessing code is outside of the main class, it works fine but the class can't use the data it retrives. When I put the mutlithreading code insdie the class, I run into an EOFError: Ran out of input
error.
代码如下:
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
With some debugging, the problem happens at proc.start()
but proc
has data. The lists have data too, which is why I am confused why it is running out of input. Note: in my code there are six entries in the gas_list
解决方案
你不能像那样使用多处理(嗯,你可以,但结果将是不可预测的) - 当你创建一个新进程时你的 minimalmodbus.Instrument列表中的
对象不会作为引用传递,而是作为一个全新的对象传递.每当您 start()
multiprocess.Process
实例时,Python 本质上都会运行一个全新的 Python 解释器实例,并且由于不同的进程获得不同的堆栈,它们无法共享内部内存所以 Python 实际上会腌制传递的参数,将它们发送到进程,然后在那里解开它们,从而产生两个进程(父进程和子进程)具有相同数据的错觉.
You cannot use multiprocessing like that (well, you can, but the result will be unpredictable) - when you create a new process your minimalmodbus.Instrument
object from the list doesn't get passed as a reference but as a whole new object. Python essentially runs a completely new Python interpreter instance whenever you start()
a multiprocess.Process
instance and since different processes get different stacks they don't get to share the internal memory so Python actually pickles the passed arguments, sends them to the Process and then unpickles them there creating an illusion that both processes (the parent and the child) have the same data.
如果您调用 self.main_reader(pickle.loads(pickle.dumps(sen)), val)
multiprocessing.Process,您可以自己观察它> (val
也会被腌制,但作为泛型,它在这里并不重要).
You can observe it yourself if instead of creating a new multiprocessing.Process
you call self.main_reader(pickle.loads(pickle.dumps(sen)), val)
(val
also gets pickled but as a generic it's not of any importance here).
同样的过程也发生在 Application.main_reader()
方法(虽然定义很奇怪)——你设置它的方式是你的整个 Application
实例实际上在子进程中被重新创建,以便 Python 可以调用它的 main_reader()
方法.
The very same process happens to the Application.main_reader()
method (although weirdly defined), too - the way you have it set up is that your whole Application
instance actually gets recreated in the sub-process so that Python can call its main_reader()
method.
您可以做的是将重新创建原始对象所需的参数传递给子流程函数,然后在函数启动时创建您的对象.例如,如果您将 Application.all_process()
方法修改为:
What you can do instead is to pass needed arguments to recreate your original object to the sub-process function, and then have your object created when your function starts. For example, if you modify your Application.all_process()
method as:
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()
函数:
And then have your main_reader()
function defined outside of the Application
class as:
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
- 我不知道您试图用它实现什么,但它肯定不会像您认为的那样做.
It should stop throwing errors. Also, you've used threading.Lock
in your original code - I don't know what you were trying achieve with it, but it most certainly doesn't do what you think it does.
相关文章