You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python使用multiprocessing调用ctypes报can't pickle local object错误求助

问题根因

你遇到的报错本质是multiprocessing的进程间通信依赖pickle序列化,而你在Phasemeter类的__init__方法中提前加载了ctypes.CDLL对象,CDLL内部的函数指针_FuncPtr属于ctypes内部定义的局部对象,不支持pickle序列化。你把整个Phasemeter实例作为参数传入Process.map的时候,序列化步骤就会失败。你之前把lambda改成全局函数没有生效,是因为问题根源不在调用的函数,而在于要传递的参数本身包含不可序列化的属性。

解决方案

最简单的修改方式是延迟加载CDLL动态库,把CDLL加载、函数签名配置的逻辑从__init__方法移到start方法内,这样Phasemeter实例在__init__执行完成后,不会携带任何不可序列化的ctypes对象,序列化传递的时候就不会报错,每个子进程在执行start方法时会独立加载动态库,符合多进程的地址空间隔离规则,也不会影响采集功能的正常运行。

修改后的参考代码如下:

import multiprocessing
import os
import ctypes

# 全局启动函数
def start(Phasemeter_object):
    Phasemeter_object.start()
        
class Phasemeter :
    # 所有相位计实例的全局列表
    phase_list=[]
    
    def __init__(self, name, path_to_so_file="./phasemeter.so", options=None, encoded_path_to_log_file=None, *args,**kwargs) :
        # 仅存储可序列化的基础配置参数,不加载CDLL
        self.name = name
        self.path_to_so_file = path_to_so_file
        self.options = options if options is not None else []
        self.encoded_path_to_log_file = encoded_path_to_log_file
        self.path=os.path.abspath(os.path.join(os.getcwd(), self.path_to_so_file))
        # 将实例加入全局列表
        Phasemeter.phase_list.append(self)

    def start(self):
        # CDLL加载、签名配置逻辑移到start方法内,由子进程独立执行
        self.LIBC = ctypes.CDLL(self.path)
        self.LIBC.lisaf_phasemeter_main.argtypes= [ctypes.c_int,
                                            ctypes.POINTER(ctypes.c_char_p),
                                            ctypes.c_char_p,]
        self.LIBC.lisaf_phasemeter_main.restypes = [ctypes.c_int,]
        
        # 原有参数处理、调用C库的逻辑不变
        self.c_char_pointer_array = ctypes.c_char_p * len(self.options)
        self.encoded_options = [str.encode(str(i)) for i in self.options ]
        self.encoded_options = self.c_char_pointer_array (*self.encoded_options)

        if self.encoded_path_to_log_file :
            self.status = self.LIBC.lisaf_phasemeter_main(
                len(self.encoded_options),
                self.encoded_options,
                self.encoded_path_to_log_file)
        else :
            self.status = self.LIBC.lisaf_phasemeter_main(len(self.encoded_options),
                                                      self.encoded_options,None)

if __name__ == '__main__' :
    my_phase1 = Phasemeter(name="PH1")
    my_phase2 = Phasemeter(name="PH2")
    with multiprocessing.Pool(len(Phasemeter.phase_list)) as Process :
        Process.map(start,Phasemeter.phase_list)

如果你确实需要在主进程提前加载CDLL,也可以给Phasemeter类自定义序列化规则,添加__getstate__和__setstate__方法实现:

class Phasemeter:
    # 原有类逻辑不变,新增以下两个方法
    def __getstate__(self):
        # 序列化时排除不可序列化的CDLL属性
        state = self.__dict__.copy()
        if 'LIBC' in state:
            del state['LIBC']
        return state
    
    def __setstate__(self, state):
        # 反序列化时重新加载CDLL、配置函数签名
        self.__dict__.update(state)
        self.LIBC = ctypes.CDLL(self.path)
        self.LIBC.lisaf_phasemeter_main.argtypes= [ctypes.c_int, ctypes.POINTER(ctypes.c_char_p), ctypes.c_char_p,]
        self.LIBC.lisaf_phasemeter_main.restypes = [ctypes.c_int,]

内容的提问来源于stack exchange,提问作者Hugo Castaing

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 18:06:03