具有多处理的 pyserial 给了我一个 ctype 错误



嗨,我正在尝试编写一个模块,让我通过pyserial读取和发送数据。我必须能够与主脚本并行读取数据。在堆栈溢出用户的帮助下,我有一个基本的和工作的程序框架,但是当我尝试添加我创建的使用 pyserial (处理查找端口、速度等)创建的类时,我收到以下错误:

File "<ipython-input-1-830fa23bc600>", line 1, in <module>
runfile('C:.../pythonInterface1/Main.py', wdir='C:/Users/Daniel.000/Desktop/Daniel/Python/pythonInterface1')
File "C:...Anaconda3libsite-packagesspyder_kernelscustomizespydercustomize.py", line 827, in runfile
execfile(filename, namespace)
File "C:...Anaconda3libsite-packagesspyder_kernelscustomizespydercustomize.py", line 110, in execfile
exec(compile(f.read(), filename, 'exec'), namespace)
File "C:/Users/Daniel.000/Desktop/Daniel/Python/pythonInterface1/Main.py", line 39, in <module>
p.start()
File "C:...Anaconda3libmultiprocessingprocess.py", line 112, in start
self._popen = self._Popen(self)
File "C:...Anaconda3libmultiprocessingcontext.py", line 223, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "C:...Anaconda3libmultiprocessingcontext.py", line 322, in _Popen
return Popen(process_obj)
File "C:...Anaconda3libmultiprocessingpopen_spawn_win32.py", line 89, in __init__
reduction.dump(process_obj, to_child)
File "C:...Anaconda3libmultiprocessingreduction.py", line 60, in dump
ForkingPickler(file, protocol).dump(obj)
ValueError: ctypes objects containing pointers cannot be pickled

这是我用来调用类的代码SerialConnection.py

import multiprocessing 
from time import sleep
from operator import methodcaller
from SerialConnection import SerialConnection as SC
class Spawn:
def __init__(self, _number, _max):
self._number = _number
self._max = _max
# Don't call update here
def request(self, x):
print("{} was requested.".format(x))
def update(self):
while True:
print("Spawned {} of {}".format(self._number, self._max))
sleep(2)
if __name__ == '__main__':
'''
spawn = Spawn(1, 1)  # Create the object as normal
p = multiprocessing.Process(target=methodcaller("update"), args=(spawn,)) # Run the loop in the process
p.start()
while True:
sleep(1.5)
spawn.request(2)  # Now you can reference the "spawn"
'''
device = SC()
print(device.Port)
print(device.Baud)
print(device.ID)
print(device.Error)
print(device.EMsg)
p = multiprocessing.Process(target=methodcaller("ReadData"), args=(device,)) # Run the loop in the process
p.start()
while True:
sleep(1.5)
device.SendData('0003')

我做错了什么,这门课给我带来了麻烦?同时使用pyserial和多处理是否有某种形式的限制?我知道这是可以做到的,但我不明白如何...

这是我从Python得到的回溯

Traceback (most recent call last):   File "C:...PythonpythonInterface1Main.py", line 45, in <module>
p.start()
File "C:...AppDataLocalProgramsPythonPython36-32libmultiprocessingprocess.py", line 105, in start
self._popen = self._Popen(self)
File "C:...AppDataLocalProgramsPythonPython36-32libmultiprocessingcontext.py", line 223, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "C:...AppDataLocalProgramsPythonPython36-32libmultiprocessingcontext.py", line 322, in _Popen
return Popen(process_obj)
File "C:...AppDataLocalProgramsPythonPython36-32libmultiprocessingpopen_spawn_win32.py", line 65, in __init__
reduction.dump(process_obj, to_child)
File "C:...AppDataLocalProgramsPythonPython36-32libmultiprocessingreduction.py", line 60, in dump
ForkingPickler(file, protocol).dump(obj) ValueError: ctypes objects containing pointers cannot be pickled

您正在尝试将SerialConnection实例作为参数传递给另一个进程。为此,python必须首先序列化(pickle)对象,并且不可能SerialConnection对象。

正如 Rob Street ing 的回答中所述,一个可能的解决方案是允许使用调用multiprocessing.Process.start时发生的分叉将SerialConnection对象复制到另一个进程的内存中,但这在 Windows 上不起作用,因为它不使用分叉。

在代码中实现并行性的一种更简单、跨平台且更有效的方法是使用线程而不是进程。对代码的更改很少:

import threading
p = threading.Thread(target=methodcaller("ReadData"), args=(device,))

我认为问题是由于device内部的某些东西不可挑选(即无法通过python序列化)。查看此页面,看看是否可以看到设备对象中的某些内容可能破坏的任何规则。

那么,为什么device需要可挑呢?

当多处理时。进程启动后,它在操作系统级别使用 fork()(除非另有说明)来创建新进程。这意味着父进程的整个上下文被"复制"到子进程。这不需要酸洗,因为它是在操作系统级别完成的。

(注意:至少在Unix上,这个"复制"实际上是一个非常便宜的操作,因为它使用了一个名为"copy-on-write"的功能。这意味着父进程和子进程实际上都从同一内存中读取,直到其中一个或另一个修改它,此时原始状态被复制到子进程。

但是,您希望进程处理的函数的参数必须被挑剔,因为它们不是主进程上下文的一部分。因此,这包括您的device变量。

我认为您可以通过允许将device作为分叉操作的一部分进行复制而不是将其作为变量传递来解决您的问题。但是,要做到这一点,您需要围绕您希望进程执行的操作使用包装器函数,在本例中为methodcaller("ReadData")。像这样:

if __name__ == "__main__":
device = SC()
def call_read_data():
device.ReadData()
...
p = multiprocessing.Process(target=call_read_data) # Run the loop in the process
p.start()

相关内容

  • 没有找到相关文章

最新更新