问题:
我得到了以下的回溯信息,但不明白其含义或如何解决:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Python26\lib\multiprocessing\forking.py", line 342, in main
self = load(from_parent)
File "C:\Python26\lib\pickle.py", line 1370, in load
return Unpickler(file).load()
File "C:\Python26\lib\pickle.py", line 858, in load
dispatch[key](self)
File "C:\Python26\lib\pickle.py", line 1083, in load_newobj
obj = cls.__new__(cls, *args)
TypeError: object.__new__(pyodbc.Cursor) is not safe, use pyodbc.Cursor.__new__()
情况:
我有一个装满需要处理的数据的 SQL Server 数据库。我试图使用 multiprocessing 模块来并行化处理,利用计算机上的多个核心。我的一般类结构如下:
- MyManagerClass
- 这是主要的类,程序从这里开始。
- 它创建两个 multiprocessing.Queue 对象,一个是
work_queue
,另一个是write_queue
- 它还创建和启动其他进程,然后等待它们完成。
- 注意:这不是 multiprocessing.managers.BaseManager() 的扩展
- MyReaderClass
- 这个类从 SQL Server 数据库中读取数据。
- 它将项目放入
work_queue
中。
- MyWorkerClass
- 这是工作处理发生的地方。
- 它从
work_queue
获取项目,并将已完成的项目放入write_queue
中。
- MyWriterClass
- 这个类负责将处理后的数据写回 SQL Server 数据库。
- 它从
write_queue
获取项目。
想法是会有一个管理器、一个读者、一个写者和许多工人。
其他细节:
我在 stderr 中得到了两次 traceback,所以我认为它会在读者和写者那里发生一次。我的工人进程被成功创建,但是只会坐在那里,直到我发送 KeyboardInterrupt ,因为它们没有在 work_queue
中找到任务。
读者和写者都有自己的数据库连接,在初始化时创建。
解决方法:
感谢 Mark 和 Ferdinand Beyer 的答案和问题,他们指出 Cursor 对象不可“pickle-able”,这是 multiprocessing 用来在进程之间传递信息的方法。
我的代码问题在于 MyReaderClass(multiprocessing.Process)
和 MyWriterClass(multiprocessing.Process)
在它们的 __init__()
方法中都连接到数据库。我在 MyManagerClass
中创建了这两个对象(即调用了它们的 init 方法),然后调用了 start()
。
因此,它会创建连接和游标对象,然后尝试通过 pickle 将它们发送到子进程。我的解决方案是将连接和游标对象的实例化移动到 run() 方法中,该方法在子进程完全创建之前不会被调用。