死锁是因为阻塞了Queue.get()方法

时间:2013-12-11 00:51:10

标签: python python-2.7 queue multiprocessing producer-consumer

正如标题暗示我有一个僵局,不知道为什么。我有多个生产者,只有一个消费者。在线程调用队列的schedule_task方法后,get方法将被多个进程调用

from logging import getLogger
from time import sleep
from threading import Event, Thread
from multiprocessing import Process
from Queue import Queue


class TaskExecutor(object):
    def __init__(self):
        print("init taskExecutor")
        self.event = Event()
        self.taskInfos = Queue()
        task_thread = Thread(target=self._run_worker_thread)
        self._instantEnd = False
        self._running = True
        task_thread.daemon = True
        task_thread.start()

    def _run_worker_thread(self):
        print("start running taskExcecutor worker Thread")
        while self.is_running():
            try:
                print("try to get queued task from %s" % str(self.taskInfos))
                msg, task = self.taskInfos.get()
                print("got task %s for msg: %s" % str(task), str(msg))
                task.execute(msg)
                self.taskInfos.task_done()
            except Exception, e:
                logger.error("Error: %s" % e.message)
        print("shutting down TaskExecutor!")

    def is_running(self):
        return True

    def schedule_task(self, msg, task):
        try:
            print("appending task '%s' for msg: %s" % (str(task), str(msg)))
            self.taskInfos.put((msg, task))
            print("into queue: %s " % str(self.taskInfos))
        except Exception, e:
            print("queue is probably full: %s" % str(e))


class Task(object):

    def execute(self, msg):
        print(msg)


executor = TaskExecutor()

def produce():
    cnt = 0
    while True:
        executor.schedule_task("Message " + str(cnt), Task())
        cnt += 1
        sleep(1)

for i in range(4):
    p = Process(target=produce)
    p.start()

从我的日志中我得到:

init taskExecutor
start running taskExcecutor worker Thread
try to get queued task from <Queue.Queue instance at 0x7fdd09830cb0>
 appending task '<__main__.Task object at 0x7fdd098f8f10>' for msg: Message 0
into queue: <Queue.Queue instance at 0x7fdd09830cb0> 
appending task '<__main__.Task object at 0x7fdd098f8f10>' for msg: Message 0
into queue: <Queue.Queue instance at 0x7fdd09830cb0> 
appending task '<__main__.Task object at 0x7fdd098f8f10>' for msg: Message 0
into queue: <Queue.Queue instance at 0x7fdd09830cb0> 
appending task '<__main__.Task object at 0x7fdd098f8f10>' for msg: Message 0
into queue: <Queue.Queue instance at 0x7fdd09830cb0> 
appending task '<__main__.Task object at 0x7fdd086f35d0>' for msg: Message 1
into queue: <Queue.Queue instance at 0x7fdd09830cb0> 
appending task '<__main__.Task object at 0x7fdd086f3490>' for msg: Message 1
into queue: <Queue.Queue instance at 0x7fdd09830cb0> 
appending task '<__main__.Task object at 0x7fdd086f3b10>' for msg: Message 1
into queue: <Queue.Queue instance at 0x7fdd09830cb0> 
appending task '<__main__.Task object at 0x7fdd086f3b10>' for msg: Message 1
into queue: <Queue.Queue instance at 0x7fdd09830cb0> 

有人可以解释一下,我错过了什么?

1 个答案:

答案 0 :(得分:3)

虽然其他人无法运行此代码(它不是自包含的),但您展示的内容并没有明显的问题。所以问题在于你没有显示的东西 - 可能在代码创建和使用TaskExecutor的实例中。

当我插入缺少的部分时,我凭空捏造,这段代码运行良好。

所以你需要展示的不仅仅是这个。如何更换:

logger.debug("try to get queued task")

logger.debug("try to get queued task from queue %s", self.taskInfos)

?然后,至少我们可以看到您的生产者是否正在使用与您的消费者相同的队列。

下一步

感谢您添加。接下来:这是一个独立的程序供您试用。它非常像你的代码。看看它是否正确运行(对我而言):

from threading import Thread, Lock
from Queue import Queue

class Logger:
     def __init__(self):
         self.iolock = Lock()

     def debug(self, str, *msg):
         with self.iolock:
             print str % msg

     error = debug

logger = Logger()

class TaskExecutor(object):
    def __init__(self):
        logger.debug("init taskExecutor")
        self.taskInfos = Queue()
        task_thread = Thread(target=self._run_worker_thread)
        task_thread.daemon = True
        task_thread.start()

    def is_running(self):
        return True

    def _run_worker_thread(self):
        logger.debug("start running taskExcecutor worker Thread")
        while self.is_running():
            try:
                logger.debug("try to get queued task from queue %s", self.taskInfos)
                msg, task = self.taskInfos.get()
                logger.debug("got task %s for msg: %s", str(task), str(msg))
                #task.execute(msg)
                self.taskInfos.task_done()
            except Exception, e:
                logger.error("Error: %s", e.message)
        logger.debug("shutting down TaskExecutor!")

    def schedule_task(self, msg, task):
        try:
            logger.debug("appending task '%s' for msg: %s", str(task), str(msg))
            self.taskInfos.put((msg, task))
            logger.debug("into queue: %s ", str(self.taskInfos))
        except Exception, e:
            logger.debug("queue is probably full: %s", str(e))

te = TaskExecutor()

def runit():
    for i in range(10):
        te.schedule_task("some task", i)

main = Thread(target=runit)
main.start()

下一步

好的,这段代码可能无法正常工作。在Linux-y系统上,只创建了TaskExecutor的一个实例,此处为:

executor = TaskExecutor()

这在主程序中发生。每次你这样做:

p = Process(target=produce)

您的主要计划是fork()。虽然分叉流程也会看到executor,但它是主程序executor的地址空间副本,并且没有任何关系主程序中的executor(通常的写时复制fork()语义)。

每个子流程还包含executorQueue数据成员的副本,包括其executor。所有子进程都将数据放在他们自己的executor唯一副本上,但是消费者线程在主程序中只运行 ,并且没有工作进程对他们的multiprocessing.Queue副本可能对主程序的消费者线程所看到的内容产生任何影响。

所以这真的很困惑。我现在必须停下来试图弄明白你可能想要在这里做什么;-)如果你想玩想法,请使用Queue.Queue进行调查。在进程之间进行通信的唯一方式是使用从头开始构建的对象来支持进程间通信。 from time import sleep from threading import Thread from multiprocessing import Process, JoinableQueue class TaskExecutor(Thread): def __init__(self): print("init taskExecutor") Thread.__init__(self) self.taskInfos = JoinableQueue() def getq(self): return self.taskInfos def run(self): print("start running taskExcecutor worker Thread") while self.is_running(): try: print("try to get queued task from %s" % self.taskInfos) msg, task = self.taskInfos.get() print("got task %s for msg: %s" % (task, msg)) task.execute(msg) self.taskInfos.task_done() except Exception, e: print("Error: %s" % e.message) print("shutting down TaskExecutor!") def is_running(self): return True class Task(object): def execute(self, msg): print(msg) def produce(q): cnt = 0 while True: q.put(("Message " + str(cnt), Task())) cnt += 1 sleep(1) if __name__ == "__main__": executor = TaskExecutor() executor.start() for i in range(4): p = Process(target=produce, args=(executor.getq(),)) p.start() 永远不会为此工作。

还有一个

这是一个适用于各个流程,甚至在Windows上运行良好的一个; - )

__name__ == "__main__"

if executor部分不允许它在Windows上运行,它有很好的&#34;文档&#34;价值也是如此,一目了然 task.execute(msg) 确实只在主程序中运行

但问题是,这是否是您想要的分工。你真的想要主要过程 - 只有主要过程 - 来做所有的

schedule_task()

工作?从这里无法猜测这是否是你想要的。这就是代码的作用。

样式点:请注意,这摆脱了{{1}}方法。并行处理可能很困难,并且几十年来我发现保持线程间/进程间通信尽可能简单和死亡非常有价值。这意味着,除其他外,直接使用消息队列而不是例如将它们隐藏在方法中。在这种情况下,抽象层通常会使正确的代码更难来创建,扩展,调试和维护。