我在python 2.7(在linux下)编写了一个类,它使用多个进程异步操作数据库。使用multiprocessing.Queue.put()
和multiprocessing.Queue.get()
时我遇到了一种非常奇怪的阻止行为,我无法解释。
以下是我所做的简化版本:
from multiprocessing import Process, Queue
class MyDB(object):
def __init__(self):
self.inqueue = Queue()
p1 = Process(target = self._worker_process, kwargs={"inqueue": self.inqueue})
p1.daemon = True
started = False
while not started:
try:
p1.start()
started = True
except:
time.sleep(1)
#Sometimes I start a same second process but it makes no difference to my problem
p2 = Process(target = self._worker_process, kwargs={"inqueue": self.inqueue})
#blahblah... (same as above)
@staticmethod
def _worker_process(inqueue):
while True:
#--------------this blocks depite data having arrived------------
op = inqueue.get(block = True)
#do something with specified operation
#---------------problem area end--------------------
print "if this text gets printed, the problem was solved"
def delete_parallel(self, key, rawkey = False):
someid = ...blahblah
#--------------this section blocked when I was posting the question but for unknown reasons it's fine now
self.inqueue.put({"optype": "delete", "kwargs": {"key":key, "rawkey":rawkey}, "callid": someid}, block = True)
#--------------problem area end----------------
print "if you see this text, there was no blocking or block was released"
如果我在测试中运行上面的代码(我在delete_parallel
对象上调用MyDB
),那么一切正常,但如果我在整个应用程序的上下文中运行它(导入其他东西) ,包容性pygtk)奇怪的事情发生:
由于某种原因,self.inqueue.get
阻止并且永远不会释放,尽管self.inqueue
在其缓冲区中有数据。当我改为调用self.inqueue.get(block = False, timeout = 1)
时,尽管队列包含数据,但是通过提升Queue.Empty来完成调用。 qsize()
返回1(建议数据存在),而empty()
返回True(表示没有数据)。
现在很明显,我的应用程序中必须存在其他某些内容,它会导致self.inqueue
无法通过获取某些内部信号量而无法使用。但是,我不知道该寻找什么。一旦达到阻塞信号量,Eclipse dubugging就变得毫无用处。
编辑8 (清理并总结我以前的编辑)上次遇到类似的问题时,结果发现pygtk正在劫持全局解释器锁,但我通过调用{{1在我打电话之前。这个问题可能有关系吗?
当我在gobject.threads_init()
方法之后引入print "successful reception"
并在终端中执行我的应用程序时,首先会发生相同的行为。当我按CTRL + D终止时,我突然得到字符串"成功接收" inbetween消息。这看起来像一些其他进程/线程被终止并释放锁定阻塞get()
进程的锁。
由于卡住的进程稍后终止,我仍然会看到该消息。什么样的过程可以从外部弄乱这样的队列? get()
只能在我班级内访问。
现在它似乎归结为这个队列,尽管有数据存在但仍无法返回任何内容:
self.inqueue
方法在尝试从某个内部管道接收实际数据时似乎卡住了。调试器挂起之前的最后一行是:
get()
位于res = self._recv()
内
跟踪这个内部python的东西,我找到了分配
multiprocessing.queues.get()
和self._recv = self._reader.recv
。
编辑9
我目前正试图追捕导致它的导入。我的应用程序非常复杂,有数百个类,每个类导入很多其他类,所以这是一个非常痛苦的过程。我找到了第一个候选类,当我跟踪它的所有导入时使用3个不同的self._reader, self._writer = Pipe(duplex=False)
个实例(但据我所知,它在任何时候都不能访问MyDB
。奇怪的是,它基本上只是一个包装器,并且包装类在单独导入时工作得很好。这也意味着它使用MyDB.inqueue
而不冻结。一旦我导入包装器(导入该类),我就会遇到阻塞问题。
我开始通过逐步重用旧代码来重写包装器。我每次引入几行新行时都要测试,直到我希望看到哪一行会导致问题返回。
答案 0 :(得分:0)
queue.Queue
使用内部线程来维护其状态。如果您使用GTK,那么它将破坏这些线程。因此,您需要致电gobject.init_threads()
。
应该注意,qsize()
仅返回队列的近似大小。实际大小可以是介于0和qsize()
返回的值之间的任何位置。