Python多进程与pyodbc数据库访问“安全吗”?
问题:
我遇到了以下错误信息,但不太明白这是什么意思,也不知道该怎么解决:
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数据库,里面有很多需要处理的数据。我想用多进程模块来并行处理这些工作,以充分利用我电脑的多个核心。我的类结构大致如下:
- MyManagerClass
- 这是主类,程序从这里开始运行。
- 它创建了两个多进程队列,一个叫
work_queue
,另一个叫write_queue
。 - 它还创建并启动了其他进程,然后等待它们完成。
- 注意:这不是 multiprocessing.managers.BaseManager() 的扩展
- MyReaderClass
- 这个类负责从SQL Server数据库中读取数据。
- 它把数据放到
work_queue
中。
- MyWorkerClass
- 这个类负责处理工作。
- 它从
work_queue
中获取数据,并把处理完成的数据放到write_queue
中。
- MyWriterClass
- 这个类负责把处理好的数据写回到SQL Server数据库。
- 它从
write_queue
中获取数据。
我的想法是有一个管理者、一个读取者、一个写入者和多个工作者。
其他细节:
我在错误输出中看到这个错误信息出现了两次,所以我猜测是读取者和写入者各出现了一次。我的工作进程创建得很好,但它们就在那里等待,直到我按下键盘中断,因为 work_queue
中没有任何东西。
读取者和写入者各自都有自己的数据库连接,这些连接是在初始化时创建的。
解决方案:
感谢Mark和Ferdinand Beyer的回答和问题,帮助我找到了这个解决方案。他们指出了Cursor对象不能被“序列化”,而这是多进程在进程间传递信息时使用的方法。
我代码中的问题在于 MyReaderClass(multiprocessing.Process)
和 MyWriterClass(multiprocessing.Process)
都在它们的 __init__()
方法中连接到数据库。我在 MyManagerClass
中创建了这两个对象(也就是调用了它们的初始化方法),然后调用了 start()
。
这样就会创建连接和游标对象,然后尝试通过序列化把它们发送到子进程。我的解决方案是把连接和游标对象的创建移动到 run()
方法中,这个方法只有在子进程完全创建后才会被调用。
3 个回答
这个错误是在 pickle
模块中出现的,也就是说你的数据库游标对象在某个地方被转存(也就是把它变成可以存储的格式)和恢复(再把它变回Python对象)。
我猜 pyodbc.Cursor
这个对象不支持转存。你为什么要试着保存游标对象呢?
检查一下你在工作流程中是否使用了 pickle
,或者它是否被隐含地使用了。
多进程编程依赖于“序列化”来在不同的进程之间传递对象。可是,pyodbc的连接和游标对象是不能被序列化的。
>>> cPickle.dumps(aCursor)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python2.5/copy_reg.py", line 69, in _reduce_ex
raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle Cursor objects
>>> cPickle.dumps(dbHandle)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python2.5/copy_reg.py", line 69, in _reduce_ex
raise TypeError, "can't pickle %s objects" % base.__name__
TypeError: can't pickle Connection objects
“它把东西放进工作队列”,那到底是什么东西呢?有可能游标对象也被传递过去了吗?