我试图在python中使用池运行dll-library并遇到以下问题。我创建了一个简单的dll库来说明这个问题。下面是dll库的源代码,它只包含一个函数,用于对两个双精度数求和:
extern "C" {
double sum(double x, double y);
}
double sum(double x, double y) {
return x + y;
}
我在Linux系统上使用
编译它g++ -fPIC -c dll_main.cpp
g++ dll_main.o -shared -o sum_dll.so
我在以下Python脚本中使用这个dll库:
#!/usr/bin/env python3.8
from ctypes import *
import multiprocessing
from multiprocessing import Pool, freeze_support
def run_dll(dll_obj, x, y):
x_c = c_double(x)
y_c = c_double(y)
z = dll_obj.sum(x_c, y_c)
return z
def main():
pool = Pool(processes=2)
dll_obj = cdll.LoadLibrary('./sum_dll.so')
dll_obj.sum.restype = c_double
z = pool.map(run_dll, [(dll_obj, 2, 3), (dll_obj, 3, 4)])
pool.close()
pool.join()
if __name__ == "__main__":
freeze_support()
main()
我得到以下错误信息:
Traceback (most recent call last):
File "./run_dll.py", line 24, in <module>
main()
File "./run_dll.py", line 17, in main
z = pool.map(run_dll, [(dll_obj, 2, 3), (dll_obj, 3, 4)])
File "/usr/lib/python3.8/multiprocessing/pool.py", line 364, in map
return self._map_async(func, iterable, mapstar, chunksize).get()
File "/usr/lib/python3.8/multiprocessing/pool.py", line 768, in get
raise self._value
File "/usr/lib/python3.8/multiprocessing/pool.py", line 537, in _handle_tasks
put(task)
File "/usr/lib/python3.8/multiprocessing/connection.py", line 206, in send
self._send_bytes(_ForkingPickler.dumps(obj))
File "/usr/lib/python3.8/multiprocessing/reduction.py", line 51, in dumps
cls(buf, protocol).dump(obj)
AttributeError: Can't pickle local object 'CDLL.__init__.<locals>._FuncPtr'
我做错了什么?如何使用dll库与几个进程在python正确?
你不能pickle dll指针,因为访问dll需要一个系统调用将它们链接到你的进程。
由于不能将DLL函数作为参数传递,因此需要将它们封装在python函数中,因此应该在全局作用域
中定义此函数,与定义普通python函数相同。from ctypes import *
import multiprocessing
from multiprocessing import Pool, freeze_support
dll_obj = cdll.LoadLibrary('./sum_dll.so')
dll_obj.sum.restype = c_double
def pyrun_dll(x, y): # looks for the dll in global scope
x_c = c_double(x)
y_c = c_double(y)
z = dll_obj.sum(x_c, y_c)
return z
def dll_sum(x_c, y_c): # pickleable wrapper
return dll_obj.sum(x_c, y_c)
def run_dll(py_sum, x, y): # arguments must be pickleable
x_c = c_double(x)
y_c = c_double(y)
z = py_sum(x_c, y_c)
return z
然后在主目录
中调用这些python函数def main():
pool = Pool(processes=2)
z = pool.starmap(pyrun_dll, [(2, 3), (3, 4)])
z2 = pool.starmap(run_dll, [(dll_sum, 2, 3), (dll_sum, 3, 4)])
pool.close()
pool.join()
if __name__ == "__main__":
freeze_support()
main()
上述代码的执行方式取决于您的操作系统,但这将在所有平台上工作(假设您更改了每个平台的DLL名称),因为它们要么从全局作用域派生DLL引用,要么在导入文件时创建一个。
你的原始函数将工作,如果你传递dll_sum
给它,而不是dll原始函数句柄。
如果你需要动态加载你的dll,那么你应该让每个进程调用dll来链接到dll本身(通常通过初始化器),而不是通过函数参数获得它。