【问题标题】:pyserial with multiprocessing gives me a ctype error具有多处理功能的 pyserial 给了我一个 ctype 错误
【发布时间】:2019-08-24 17:52:29
【问题描述】:

您好,我正在尝试编写一个模块,让我可以通过pyserial 读取和发送数据。我必须能够与我的主脚本并行读取数据。在stackoverflow用户的帮助下,我有一个the program的基本和工作骨架,但是当我尝试添加一个使用pyserial(查找端口、速度等的句柄)创建的类时发现here我收到以下错误:

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:...\Anaconda3\lib\site-packages\spyder_kernels\customize\spydercustomize.py", line 827, in runfile
    execfile(filename, namespace)

  File "C:...\Anaconda3\lib\site-packages\spyder_kernels\customize\spydercustomize.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:...\Anaconda3\lib\multiprocessing\process.py", line 112, in start
    self._popen = self._Popen(self)

  File "C:...\Anaconda3\lib\multiprocessing\context.py", line 223, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)

  File "C:...\Anaconda3\lib\multiprocessing\context.py", line 322, in _Popen
    return Popen(process_obj)

  File "C:...\Anaconda3\lib\multiprocessing\popen_spawn_win32.py", line 89, in __init__
    reduction.dump(process_obj, to_child)

  File "C:...\Anaconda3\lib\multiprocessing\reduction.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:...\Python\pythonInterface1\Main.py", line 45, in <module>
    p.start()

  File "C:...\AppData\Local\Programs\Python\Python36-32\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)

  File "C:...\AppData\Local\Programs\Python\Python36-32\lib\multiprocessing\context.py", line 223, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)

  File "C:...\AppData\Local\Programs\Python\Python36-32\lib\multiprocessing\context.py", line 322, in _Popen
    return Popen(process_obj)

  File "C:...\AppData\Local\Programs\Python\Python36-32\lib\multiprocessing\popen_spawn_win32.py", line 65, in __init__
    reduction.dump(process_obj, to_child)

  File "C:...\AppData\Local\Programs\Python\Python36-32\lib\multiprocessing\reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj) ValueError: ctypes objects containing pointers cannot be pickled

【问题讨论】:

  • 你最好不要在 anaconda 环境中运行。 Anaconda 有时会破坏东西。
  • 是的,我注意到了,不幸的是 spyder 是通过 anaconda 安装的,但我尝试在空闲状态下运行它,并使用不同的 python 版本从命令行运行它,但效果不佳
  • 你能粘贴那个错误回溯而不是 anaconda 吗?我建议切换 vs 代码或 pycharm 而不是 spyder。 Anaconda 最终可能会杀了你。
  • 我添加了从 python 3.6 获得的回溯
  • 我嘲笑了Device,但似乎无法通过这样做得到错误。您能否尝试压缩和/或重写您的代码,以使错误仍然发生但不需要任何设备连接?

标签: python python-3.x multiprocessing pickle pyserial


【解决方案1】:

您正在尝试将 SerialConnection 实例作为参数传递给另一个进程。因为python首先要序列化(pickle)对象,而SerialConnection对象是不可能的。

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

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

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

【讨论】:

  • 谢谢我测试了把它放在一个线程中,并通过更多的推文我设法让它工作。
【解决方案2】:

我认为问题是由于device 内部的某些东西unpicklable(即,不能被 python 序列化)。查看this page,看看您是否可以看到任何可能被您的设备对象中的某些东西破坏的规则。

那么为什么device 需要完全可腌制?

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

(注意:至少在 unix 上,这个“复制”实际上是一个非常便宜的操作,因为它使用了一个称为“写时复制”的功能。这意味着父进程和子进程实际上都是从相同的内存,直到一个或另一个修改它,此时原始状态被复制到子进程。)

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

我认为您可以通过允许将 device 作为 fork 操作的一部分复制而不是将其作为变量传递来解决您的问题。不过,要做到这一点,您需要一个围绕您希望进程执行的操作的包装函数,在本例中为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()

【讨论】:

  • 感谢这样做让我能够在新进程中调用函数。我希望 SerialCONnection 实例负责与我的硬件通信,但我不想在实例发送或接收数据时锁定解释器。这是我使用流程的主要原因
  • 我给出的示例仍然使用单独的进程与设备交互,它只是以不同的方式将设备转移到该进程。
  • OP 正在使用 没有fork 系统调用的 ms-windows。它使用特定于 ms-windows 的系统调用来启动一个新的 Python 解释器,该解释器 导入 来自原始进程的主模块,然后启动工作函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-28
  • 2019-05-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多