扩展python Queue.PriorityQueue(工作者优先级,工作包类型)

时间:2010-10-03 08:55:02

标签: python queue parallel-processing

我想扩展此处描述的Queue.PriorityQueue:http://docs.python.org/library/queue.html#Queue.PriorityQueue

队列将保存具有优先级的工作包。工人将获得工作包并处理它们。我想做以下补充:

  1. 工人也有优先权。当多个工作人员空闲时,具有最高优先级的工作人员应处理传入的工作包。

  2. 并非每个工作人员都可以处理每个工作包,因此需要一种机制来检查工作包类型和工作人员能力是否匹配。

  3. 我正在寻找提示,如何最好地实施(从头开始,扩展PrioriyQueue或Queue,......)。

    修改

    这是我的第一次(未经测试)尝试。基本思想是将通知所有等待的线程。然后他们都试图通过_choose_worker(self, worker)获得一个工作项。 (制作社区维基)

    修改

    现在适用于一些简单的测试...

    修改 的 在BaseManager函数中添加了自定义_choose_worker和工作列表的本地副本。

    修改 的 错误修复

    import Queue
    from Queue import Empty, Full
    from time import time as _time
    import heapq
    
    class AdvancedQueue(Queue.PriorityQueue):
    
        # Initialize the queue representation
        def _init(self, _maxsize):
            self.queue = []
            self.worker = []
    
        def put(self, item, block=True, timeout=None):
            '''
            Put an item into the queue.
    
            If optional args 'block' is true and 'timeout' is None (the default),
            block if necessary until a free slot is available. If 'timeout' is
            a positive number, it blocks at most 'timeout' seconds and raises
            the Full exception if no free slot was available within that time.
            Otherwise ('block' is false), put an item on the queue if a free slot
            is immediately available, else raise the Full exception ('timeout'
            is ignored in that case).
            '''
            self.not_full.acquire()
            try:
                if self.maxsize > 0:
                    if not block:
                        if self._qsize() == self.maxsize:
                            raise Full
                    elif timeout is None:
                        while self._qsize() == self.maxsize:
                            self.not_full.wait()
                    elif timeout < 0:
                        raise ValueError("'timeout' must be a positive number")
                    else:
                        endtime = _time() + timeout
                        while self._qsize() == self.maxsize:
                            remaining = endtime - _time()
                            if remaining <= 0.0:
                                raise Full
                            self.not_full.wait(remaining)
                self._put(item)
                self.unfinished_tasks += 1
                self.not_empty.notifyAll()  # only change
            finally:
                self.not_full.release()
    
        def get(self, worker, block=True, timeout=None):
            self.not_empty.acquire()
            try:
                self._put_worker(worker)
    
                if not block:
                    if not self._qsize():
                        raise Empty
                    else:
                        return self._choose_worker(worker)
                elif timeout is None:
                    while True:
                        while not self._qsize():
                            self.not_empty.wait()
                        try:
                            return self._choose_worker(worker)
                        except Empty:
                            self.not_empty.wait()
    
                elif timeout < 0:
                    raise ValueError("'timeout' must be a positive number")
                else:
                    endtime = _time() + timeout
                    def wait(endtime):
                        remaining = endtime - _time()
                        if remaining <= 0.0:
                            raise Empty
                        self.not_empty.wait(remaining)
    
                    while True:
                        while not self._qsize():
                            wait(endtime)
    
                        try:
                            return self._choose_worker(worker)
                        except Empty:
                            wait(endtime)
            finally:
                self._remove_worker(worker)
                self.not_empty.release()
    
        # Put a new worker in the worker queue
        def _put_worker(self, worker, heappush=heapq.heappush):
            heappush(self.worker, worker)
    
        # Remove a worker from the worker queue
        def _remove_worker(self, worker):
            self.worker.remove(worker)
    
        # Choose a matching worker with highest priority
        def _choose_worker(self, worker):
            worker_copy = self.worker[:]    # we need a copy so we can remove assigned worker
            for item in self.queue:
                for enqueued_worker in worker_copy:
                    if item[1].type in enqueued_worker[1].capabilities:
                        if enqueued_worker == worker:
                            self.queue.remove(item)
                            self.not_full.notify()
                            return item
                        else:
                            worker_copy.remove(enqueued_worker)
                            # item will be taken by enqueued_worker (which has higher priority),
                            # so enqueued_worker is busy and can be removed
                            continue
            raise Empty
    

2 个答案:

答案 0 :(得分:1)

我认为你描述的情况是你有两个“优先队列” - 一个用于工作,一个用于工人。天真的方法是采取最优先的工作和最优先的工作人员,并尝试配对他们。但当然,当工人无法执行工作时,这会失败。

为了解决这个问题,我建议首先采取最优先的工作,然后按优先级递减的顺序迭代所有工作人员,直到找到可以处理该工作的工作人员。如果没有工作人员可以处理该作业,那么请执行第二个最高优先级的工作,依此类推。所以你有效地嵌套循环,如下所示:

def getNextWorkerAndJobPair():
    for job in sorted(jobs, key=priority, reverse=True):
        for worker in sorted(workers, key=priority, reverse=True):
             if worker.can_process(job):
                 return (worker, job)

上面的例子不必要地多次对数据进行排序。为避免这种情况,最好以排序顺序存储数据。至于使用什么数据结构,我不确定最好的是什么。理想情况下,您需要O(log n)插入和删除,并能够在O(n)时间内按排序顺序迭代集合。我认为PriorityQueue满足了第一个要求,但不是第二个要求。我想来blist包中的sortedlist会起作用,但我自己没有尝试过,网页并没有具体说明这个类提供的性能保证。

我建议首先迭代作业然后再遍历内循环中的工作者,这不是你可以采取的唯一方法。您还可以反转循环的顺序,以便首先选择优先级最高的工作程序,然后尝试为其找到作业。或者你可以找到一个函数f的最大值为f(priority_job,priority_worker)的有效(作业,工作者)对(例如只添加优先级)。

答案 1 :(得分:0)

唯一的答案是有用但不够详细,所以我现在接受我自己的答案。请参阅问题中的代码。