![](/img/trans.png)
[英]Python: Generate all permutations of a non-empty 2D array mixed with a separate list
[英]Python - Non-empty shared list on separate thread appears empty
我有兩個類-MessageProducer和MessageConsumer。
MessageConsumer執行以下操作:
在我的開發環境中,我面臨上述#2的問題-通過執行步驟#1添加一條消息后,當工作線程檢查“ _unprocessed_msgs”的長度時,它將變為零。 當重復步驟1時,列表會在添加了該項目的線程上正確顯示2個項目。 但是在步驟2中,在工作線程上,len(_unprocessed_msgs)再次返回零。
不知道為什么會這樣。 非常感謝幫助。
我正在使用具有Python 2.7.12的Ubuntu 16.04。
下面是示例源代碼。 如果需要更多信息,請告訴我。
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中的線程代碼時,通常會看到它以程序形式編寫:一個普通函數,該函數作為驅動每個線程的target
參數傳遞給threading.Thread
。 這樣,您無需為將具有單個實例的新類編寫代碼。
另一件事是,盡管Python的全局解釋器鎖本身保證了如果在兩個單獨的線程中進行修改,列表也不會損壞,但是列表並不是推薦的“線程數據傳遞”數據結構。 您可能應該看一下threading.Queue
來做到這一點
乍一看,這段代碼中的錯誤可能不是由於使用鎖而導致問題的原因,而可能是原因。 代替
self._unprocessed_msg_q = []
這將創建一個新的列表對象,另一個線程暫時也沒有引用(因此可能會將數據寫入舊列表),您應該執行以下操作:
self._unprocessed_msg_q[:] = []
或只是del
片件事你做其他的方法。
但是為了安全起見,並擁有模式可維護且不太令人驚訝的代碼,您確實應該在此假設Python線程更改為過程方法。 假設“線程”是可以完成其任務的“最終”對象,然后在周圍使用Queues:
# 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()
請注意,由於您希望接收線程中的多個方法可以處理數據,因此我使用了一個類-但它不繼承自Thread,因此不必擔心其工作原理。 它的所有方法都在同一個線程中調用:不需要鎖,也不必擔心接收器類內部的競爭條件。 為了在類外進行交流,Queue類的結構可以為我們處理任何競爭條件。
生產者循環,因為它只是一個虛擬生產者,根本不需要以類形式編寫。 但是,如果它有更多的方法,它將看起來一樣。
(隨機睡眠有助於可視化“現實世界”消息接收中將發生的情況)此外,您可能希望查看以下內容: https : //www.thoughtworks.com/insights/blog/composition-vs-inheritance-如何選擇
終於我解決了這個問題。 在實際的代碼中,我有一個Manager類,負責將MessageConsumerThread實例化為初始化程序中的最后一件事:
class Manager(object):
def __init__(self):
...
...
self._consumer = MessageConsumerThread(self)
self._consumer.start_msg_processing_thread()
問題似乎是當Manager仍在執行初始化程序時,在MessageConsumerThread初始化程序中傳遞了“自我”(盡管這是最后兩個步驟)。 當我將消費者的創建移出初始化程序的那一刻,消費者線程能夠看到“ _unprocessed_msg_q”中的元素。
請注意,上述示例代碼仍然無法重現該問題。 它僅在生產環境中體現。 沒有上述修復程序,我也嘗試了隊列和字典,但觀察到相同的問題。 修復后,嘗試使用隊列和列表,並能夠成功執行代碼。
我非常感謝@jsbueno和@ivan_pozdeev的時間和幫助! 社區@stackoverflow非常有幫助!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.