簡體   English   中英

Python PriorityQueue 相同優先級的項目以隨機順序獲得

[英]Python PriorityQueue items of same priority got in random order

我使用queue.Queue class 將任務從一個線程傳遞到另一個線程。 后來我需要添加優先級,所以我將其更改為PriorityQueue ,使用建議的PrioritizedItem (因為任務是 dict 並且無法比較)。 然后,在極少數情況下,它開始導致任務混淆。 我花了一段時間才意識到/調試PriorityQueue中的相同優先級項目不會保持插入順序,或者從調試的角度來看更糟糕的是,通常它們會保持插入順序。

我想,在談論任務隊列時,FIFO 是一種默認值。 這就是為什么Queue不像FifoQueue那樣被調用的原因,不是嗎? 因此, PriorityQueue應該明確 state 不是等優先級項目的 FIFO。 不幸的是,Python 文檔沒有警告我們這一點,缺乏警告讓我很頭疼,可能其他人也很頭疼。

我還沒有找到任何現成的解決方案,但我很確定其他人可能需要一個PriorityQueue來保持同等優先級項目的插入順序。 所以這張票...

此外,我希望 Python 文檔將 state 在下一個版本中發出警告,讓我分享一下我是如何解決這個問題的。

heapq (由PriorityQueue使用)建議我們需要在 item 的 compare 部分插入一個序列號,以便計算出的優先級顯而易見,避免有 2 個 item 具有相同的優先級。

我還添加了threading.Lock ,這樣我們就可以避免因為發生了一些線程競爭情況而使 2 個項目具有相同的序列號。

class _ThreadSafeCounter(object):
    def __init__(self, start=0):
        self.countergen = itertools.count(start)
        self.lock = threading.Lock()
    def __call__(self):
        with self.lock:
            return self.countergen.__next__()

#create a function that provides incremental sequence numbers
_getnextseqnum = _ThreadSafeCounter()

@dataclasses.dataclass(order=True)
class PriorityQueueItem:
    """Container for priority queue items
    
    The payload of the item is stored in the optional "data" (None by default), and
    can be of any type, even such that cannot be compared, e.g. dict.
    
    The queue priority is defined mainly by the optional "priority" argument (10 by
    default).
    If there are more items with the same "priority", their put-order is preserved,
    because of the automatically increasing sequence number, "_seqnum".
    Usage in the producer:
        pq.put(PriorityQueueItem("Best-effort-task",100))
        pq.put(PriorityQueueItem(dict(b=2))
        pq.put(PriorityQueueItem(priority=0))
        pq.put(PriorityQueueItem(dict(a=1))
    Consumer is to get the tasks with pq.get().getdata(), and will actually receive
        None
        {'b':2}
        {'a':1}
        "Best-effort-task"
    """
    data: typing.Any=dataclasses.field(default=None, compare=False)
    priority: int=10
    _seqnum: int=dataclasses.field(default_factory=_getnextseqnum, init=False)
    
    def getdata(self):
        """Get the payload of the item in the consumer thread"""
        return self.data

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM