Python-单独线程上的非空共享列表显示为空

Python-单独线程上的非空共享列表显示为空,python,multithreading,list,Python,Multithreading,List,我有两个类——MessageProducer和MessageConsumer MessageConsumer执行以下操作: 接收消息并将其放入其消息列表“\u未处理的\u msgs” 在单独的工作线程上,将消息移动到内部列表“\u in\u process\u msgs” 在工作线程上,处理来自“\u in\u process\u msgs”的消息 在我的开发环境中,我遇到了上面第2步的问题——在执行第1步添加消息后,当工作线程检查“_unprocessed_msgs”的长度时,它得到的是零。

我有两个类——MessageProducer和MessageConsumer

MessageConsumer执行以下操作:

  • 接收消息并将其放入其消息列表“\u未处理的\u msgs”
  • 在单独的工作线程上,将消息移动到内部列表“\u in\u process\u msgs”
  • 在工作线程上,处理来自“\u in\u process\u msgs”的消息
  • 在我的开发环境中,我遇到了上面第2步的问题——在执行第1步添加消息后,当工作线程检查“_unprocessed_msgs”的长度时,它得到的是零。 当重复步骤#1时,列表会在添加项目的线程上正确显示2个项目。但在步骤2中,在工作线程上,len(_unprocessed_msgs)再次返回零

    不知道为什么会这样。非常感谢您在这方面的帮助

    我使用的是Ubuntu 16.04和Python 2.7.12

    下面是示例源代码。如果需要更多信息,请告诉我

    import threading
    import time
    class MessageConsumerThread(threading.Thread):
        def __init__(self):
            super(MessageConsumerThread, self).__init__()
            self._unprocessed_msg_q = []
            self._in_process_msg_q = []
            self._lock = threading.Lock()
            self._stop_processing = False
    
        def start_msg_processing_thread(self):
            self._stop_processing = False
            self.start()
    
        def stop_msg_processing_thread(self):
            self._stop_processing = True
    
        def receive_msg(self, msg):
            with self._lock:
                LOG.info("Before: MessageConsumerThread::receive_msg: "
                         "len(self._unprocessed_msg_q)=%s" %
                         len(self._unprocessed_msg_q))
                self._unprocessed_msg_q.append(msg)
                LOG.info("After: MessageConsumerThread::receive_msg: "
                         "len(self._unprocessed_msg_q)=%s" %
                         len(self._unprocessed_msg_q))
    
        def _queue_unprocessed_msgs(self):
            with self._lock:
                LOG.info("MessageConsumerThread::_queue_unprocessed_msgs: "
                         "len(self._unprocessed_msg_q)=%s" %
                         len(self._unprocessed_msg_q))
                if self._unprocessed_msg_q:
                    LOG.info("Moving messages from unprocessed to in_process queue")
                    self._in_process_msg_q += self._unprocessed_msg_q
                    self._unprocessed_msg_q = []
                    LOG.info("Moved messages from unprocessed to in_process queue")
    
        def run(self):
            while not self._stop_processing:
                # Allow other threads to add messages to message queue
                time.sleep(1)
    
                # Move unprocessed listeners to in-process listener queue
                self._queue_unprocessed_msgs()
    
                # If nothing to process continue the loop
                if not self._in_process_msg_q:
                    continue
    
                for msg in self._in_process_msg_q:
                    self.consume_message(msg)
    
                # Clean up processed messages
                del self._in_process_msg_q[:]
    
        def consume_message(self, msg):
            print(msg)
    
    
    class MessageProducerThread(threading.Thread):
        def __init__(self, producer_id, msg_receiver):
            super(MessageProducerThread, self).__init__()
            self._producer_id = producer_id
            self._msg_receiver = msg_receiver
    
        def start_producing_msgs(self):
            self.start()
    
        def run(self):
            for i in range(1,10):
                msg = "From: %s; Message:%s" %(self._producer_id, i)
                self._msg_receiver.receive_msg(msg)
    
    
    def main():
        msg_receiver_thread = MessageConsumerThread()
        msg_receiver_thread.start_msg_processing_thread()
    
        msg_producer_thread = MessageProducerThread(producer_id='Producer-01',
                                                    msg_receiver=msg_receiver_thread)
        msg_producer_thread.start_producing_msgs()
        msg_producer_thread.join()
        msg_receiver_thread.stop_msg_processing_thread()
        msg_receiver_thread.join()
    
    if __name__ == '__main__':
        main()
    
    以下是我得到的日志:

    INFO: MessageConsumerThread::_queue_unprocessed_msgs: len(self._unprocessed_msg_q)=0
    INFO: Before: MessageConsumerThread::receive_msg: len(self._unprocessed_msg_q)=0
    INFO: After: MessageConsumerThread::receive_msg: **len(self._unprocessed_msg_q)=1**
    INFO: MessageConsumerThread::_queue_unprocessed_msgs: **len(self._unprocessed_msg_q)=0**
    INFO: MessageConsumerThread::_queue_unprocessed_msgs: len(self._unprocessed_msg_q)=0
    INFO: Before: MessageConsumerThread::receive_msg: len(self._unprocessed_msg_q)=1
    INFO: After: MessageConsumerThread::receive_msg: **len(self._unprocessed_msg_q)=2**
    INFO: MessageConsumerThread::_queue_unprocessed_msgs: **len(self._unprocessed_msg_q)=0**
    

    这对你的申请来说不是一个好的设计。 我花了一些时间试着调试这个-但是线程代码自然是复杂的,所以我们应该试着简化它,而不是让它变得更加混乱

    当我在Python中看到线程代码时,我通常会看到它是以过程形式编写的:一个传递给
    threading.Thread
    的普通函数,作为驱动每个线程的
    target
    参数。这样,您就不需要为将具有单个实例的新类编写代码

    另一件事是,尽管Python的全局解释器锁本身保证列表在两个单独的线程中修改时不会损坏,但列表不是推荐的“线程数据传递”数据结构。要做到这一点,您可能应该查看
    threading.Queue

    这段代码中的错误乍一看可能不是由于使用锁而导致问题的原因,但可能是。而不是

    self._unprocessed_msg_q = []
    
    这将创建一个新的列表对象,另一个线程也暂时没有引用(因此它可能会将数据写入旧列表),您应该执行以下操作:

    self._unprocessed_msg_q[:]  = []
    
    或者只是在另一个方法上执行
    del
    slice操作

    但为了更安全,并且有可维护的模式和不那么令人惊讶的代码,您确实应该在这里改变为过程方法,假设使用Python线程。假设“线程”是可以完成其任务的“最终”对象,然后使用以下队列:

    # coding: utf-8
    from __future__ import print_function
    from __future__ import unicode_literals
    
    from threading import Thread
    try:
        from queue import Queue, Empty
    except ImportError:
        from Queue import Queue, Empty
    import time
    import random
    
    
    TERMINATE_SENTINEL = object()
    NO_DATA_SENTINEL = object()
    
    
    class Receiver(object):
    
        def __init__(self, queue):
            self.queue = queue
            self.in_process = []
    
        def receive_data(self, data):
            self.in_process.append(data)
    
        def consume_data(self):
            print("received data:",  self.in_process)
            del self.in_process[:]
    
        def receiver_loop(self):
            queue = self.queue
            while True:
                try:
                    data = queue.get(block=False)
                except Empty:
                    print("got no data from queue")
                    data = NO_DATA_SENTINEL
    
                if data is TERMINATE_SENTINEL:
                    print("Got sentinel: exiting receiver loop")
                    break
    
                self.receive_data(data)
    
                time.sleep(random.uniform(0, 0.3))
                if queue.empty():
                    # Only process data if we have nothing to receive right now:
                    self.consume_data()
                    print("sleeping receiver")
                    time.sleep(1)
            if self.in_process:
                self.consume_data()
    
    
    def producer_loop(queue):
        for i in range(10):
            time.sleep(random.uniform(0.05, 0.4))
            print("putting {0} in queue".format(i))
            queue.put(i)
    
    
    def main():
        msg_queue = Queue()
        msg_receiver_thread = Thread(target=Receiver(msg_queue).receiver_loop)
        time.sleep(0.1)
        msg_producer_thread = Thread(target=producer_loop, args=(msg_queue,))
    
        msg_receiver_thread.start()
        msg_producer_thread.start()
        msg_producer_thread.join()
        msg_queue.put(TERMINATE_SENTINEL)
        msg_receiver_thread.join()
    
    if __name__ == '__main__':
        main()
    
    请注意,由于您希望recever线程中的多个方法处理数据,所以我使用了一个类,但它不从线程继承,也不必担心其工作方式。它的所有方法都在同一个线程中调用:不需要锁,也不担心接收方类本身的竞争条件。对于类外的通信,队列类的结构可以为我们处理任何竞争条件

    生产者循环,因为它只是一个虚拟生产者,根本不需要以类的形式编写。但如果有更多的方法,它看起来也一样

    (随机睡眠有助于想象“真实世界”信息接收中会发生什么) 此外,您可能还想看看以下内容:

    这对您的应用程序来说不是一个好的设计。 我花了一些时间试着调试这个-但是线程代码自然是复杂的,所以我们应该试着简化它,而不是让它变得更加混乱

    当我在Python中看到线程代码时,我通常会看到它是以过程形式编写的:一个传递给
    threading.Thread
    的普通函数,作为驱动每个线程的
    target
    参数。这样,您就不需要为将具有单个实例的新类编写代码

    另一件事是,尽管Python的全局解释器锁本身保证列表在两个单独的线程中修改时不会损坏,但列表不是推荐的“线程数据传递”数据结构。要做到这一点,您可能应该查看
    threading.Queue

    这段代码中的错误乍一看可能不是由于使用锁而导致问题的原因,但可能是。而不是

    self._unprocessed_msg_q = []
    
    这将创建一个新的列表对象,另一个线程也暂时没有引用(因此它可能会将数据写入旧列表),您应该执行以下操作:

    self._unprocessed_msg_q[:]  = []
    
    或者只是在另一个方法上执行
    del
    slice操作

    但为了更安全,并且有可维护的模式和不那么令人惊讶的代码,您确实应该在这里改变为过程方法,假设使用Python线程。假设“线程”是可以完成其任务的“最终”对象,然后使用以下队列:

    # coding: utf-8
    from __future__ import print_function
    from __future__ import unicode_literals
    
    from threading import Thread
    try:
        from queue import Queue, Empty
    except ImportError:
        from Queue import Queue, Empty
    import time
    import random
    
    
    TERMINATE_SENTINEL = object()
    NO_DATA_SENTINEL = object()
    
    
    class Receiver(object):
    
        def __init__(self, queue):
            self.queue = queue
            self.in_process = []
    
        def receive_data(self, data):
            self.in_process.append(data)
    
        def consume_data(self):
            print("received data:",  self.in_process)
            del self.in_process[:]
    
        def receiver_loop(self):
            queue = self.queue
            while True:
                try:
                    data = queue.get(block=False)
                except Empty:
                    print("got no data from queue")
                    data = NO_DATA_SENTINEL
    
                if data is TERMINATE_SENTINEL:
                    print("Got sentinel: exiting receiver loop")
                    break
    
                self.receive_data(data)
    
                time.sleep(random.uniform(0, 0.3))
                if queue.empty():
                    # Only process data if we have nothing to receive right now:
                    self.consume_data()
                    print("sleeping receiver")
                    time.sleep(1)
            if self.in_process:
                self.consume_data()
    
    
    def producer_loop(queue):
        for i in range(10):
            time.sleep(random.uniform(0.05, 0.4))
            print("putting {0} in queue".format(i))
            queue.put(i)
    
    
    def main():
        msg_queue = Queue()
        msg_receiver_thread = Thread(target=Receiver(msg_queue).receiver_loop)
        time.sleep(0.1)
        msg_producer_thread = Thread(target=producer_loop, args=(msg_queue,))
    
        msg_receiver_thread.start()
        msg_producer_thread.start()
        msg_producer_thread.join()
        msg_queue.put(TERMINATE_SENTINEL)
        msg_receiver_thread.join()
    
    if __name__ == '__main__':
        main()
    
    请注意,由于您希望recever线程中的多个方法处理数据,所以我使用了一个类,但它不从线程继承,也不必担心其工作方式。它的所有方法都在同一个线程中调用:不需要锁,也不担心接收方类本身的竞争条件。对于类外的通信,队列类的结构可以为我们处理任何竞争条件

    生产者循环,因为它只是一个虚拟生产者,根本不需要以类的形式编写。但如果有更多的方法,它看起来也一样

    (随机睡眠有助于想象“真实世界”信息接收中会发生什么) 此外,您可能还想看看以下内容:
    我终于解决了这个问题。在实际代码中,我有一个管理器类,负责实例化MessageConsumerThread作为其在初始值设定项中的最后一项:

    class Manager(object):
        def __init__(self):
            ...
            ...
            self._consumer = MessageConsumerThread(self)
            self._consumer.start_msg_processing_thread()
    
    问题似乎在于,当管理器仍在执行其初始值设定项时,在MessageConsumerThread初始值设定项中传递“self”(尽管这是最后两个步骤)。当我将consumer的创建移出初始值设定项时,consumer线程能够看到“_unprocessed_msg_q”中的元素

    请注意