Python-单独线程上的非空共享列表显示为空
我有两个类——MessageProducer和MessageConsumer MessageConsumer执行以下操作: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”的长度时,它得到的是零。
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”中的元素
请注意