Как проверить, находится ли задача уже в очереди python?

Я пишу простой искатель на Python, используя модули потоковой передачи и очереди. Я беру страницу, проверяю ссылки и помещаю их в очередь, когда определенный поток завершил обработку страницы, он захватывает следующий из очереди. Я использую массив для страниц, которые я уже посетил, чтобы фильтровать ссылки, которые я добавляю в очередь, но если есть несколько потоков, и они получают одинаковые ссылки на разных страницах, они помещают повторяющиеся ссылки в очередь. Итак, как я могу узнать, находится ли какой-то URL-адрес в очереди, чтобы не помещать его туда снова?

Ответы

Ответ 1

Если вам не нужен порядок, в котором обрабатываются элементы, я бы попробовал подкласс Queue, который использует set внутренне:

class SetQueue(Queue):

    def _init(self, maxsize):
        self.maxsize = maxsize
        self.queue = set()

    def _put(self, item):
        self.queue.add(item)

    def _get(self):
        return self.queue.pop()

Как отметил Пол МакГуир, это позволит добавить дублирующийся элемент после того, как он будет удален из набора для обработки, и еще не добавлен в "обработанный" набор. Чтобы решить эту проблему, вы можете хранить оба набора в экземпляре Queue, но так как вы используете больший набор для проверки того, был ли обработан элемент, вы можете вернуться к Queue, который будет правильно заказывать запросы.

class SetQueue(Queue):

    def _init(self, maxsize):
        Queue._init(self, maxsize) 
        self.all_items = set()

    def _put(self, item):
        if item not in self.all_items:
            Queue._put(self, item) 
            self.all_items.add(item)

Преимущество этого, в отличие от использования отдельного набора, заключается в том, что методы Queue являются потокобезопасными, так что вам не нужна дополнительная блокировка для проверки другого набора.

Ответ 2

Метод put также должен быть перезаписан, если не будет заблокирован вызов соединения навсегда https://github.com/python/cpython/blob/master/Lib/queue.py#L147

class UniqueQueue(Queue):

    def put(self, item, block=True, timeout=None):
        if item not in self.queue: # fix join bug
            Queue.put(self, item, block, timeout)

    def _init(self, maxsize):
        self.queue = set()

    def _put(self, item):
        self.queue.add(item)

    def _get(self):
        return self.queue.pop()

Ответ 3

Далее следует улучшение по сравнению с Lukáš Lalinský last . Важным отличием является то, что put переопределяется, чтобы обеспечить точность unfinished_tasks и join работает должным образом.

from queue import Queue

class UniqueQueue(Queue):

    def _init(self, maxsize):
        self.all_items = set()
        Queue._init(self, maxsize)

    def put(self, item, block=True, timeout=None):
        if item not in self.all_items:
            self.all_items.add(item)
            Queue.put(self, item, block, timeout)

Ответ 4

То, как я это решил (на самом деле я сделал это в Scala, а не Python), должен был использовать как Set, так и Queue, добавляя только ссылки на очередь (и набор), если они еще не существовали в наборе.

Оба набора и очередь были инкапсулированы в одном потоке, отображая только пользовательский интерфейс, похожий на очередь, на потребительские потоки.

Изменить: кто-то предложил SQLite, и это тоже то, что я рассматриваю, если набор посещенных URL-адресов должен расти. (В настоящее время каждый обход занимает всего несколько сотен страниц, поэтому он легко вписывается в память.) Но база данных - это то, что также может быть инкапсулировано внутри самого набора, поэтому потребительские потоки не должны знать об этом.

Ответ 5

SQLite настолько прост в использовании и идеально подходит... просто предложение.

Ответ 6

использование:

url in q.queue

который возвращает True iff url находится в очереди

Ответ 7

Почему только использовать массив (в идеале, словарь будет еще лучше), чтобы фильтровать все, что вы уже посетили? Добавьте вещи в свой массив/словарь, как только вы их поставите в очередь, и добавьте их только в очередь, если они еще не находятся в массиве /dict. Затем у вас есть 3 простых отдельных элемента:

  • Ссылки еще не видели (ни в очереди, ни в массиве /dict )
  • Ссылки, запланированные для посещения (как в очереди, так и в массиве /dict )
  • Ссылки, которые уже были посещены (в массиве /dict, а не в очереди)

Ответ 8

Это полная версия SetQueue

import Queue

class SetQueue(Queue.Queue):
    def _init(self, maxsize):
        Queue.Queue._init(self, maxsize)
        self.all_items = set()

    def _put(self, item):
        if item not in self.all_items:
            Queue.Queue._put(self, item)
            self.all_items.add(item)

    def _get(self):
        item = Queue.Queue._get(self)
        self.all_items.remove(item)
        return item

Ответ 9

вместо "массива уже посещенных страниц" сделайте "массив страниц, уже добавленных в очередь"

Ответ 10

К сожалению, у меня нет оценки enouch для комментариев, лучшего ответа Lukáš Lalinskýs.

Чтобы добавить поддержку SetQueue.task_done() и SetQueue.join() для второго варианта Lukáš Lalinskýs SetQueue, добавьте else brahch в if:

def _put(self, item):
    if item not in self.all_items:
        Queue._put(self, item);
        self.all_items.add(item);
    else:
        self.unfinished_tasks -= 1;

Протестировано и работает с Python 3.4.

Ответ 11

Я согласен с @Ben James. Попытайтесь использовать как deque, так и set.

вот код:

class SetUniqueQueue(Queue):

    def _init(self, maxsize):
        self.queue = deque()
        self.setqueue = set()

    def _put(self, item):
        if item not in self.setqueue:
            self.setqueue.add(item)
            self.queue.append(item)

    def _get(self):
        return self.queue.popleft()

Ответ 12

Кроме того, вместо набора вы можете попробовать использовать словарь. Операции над наборами обычно становятся довольно медленными, когда они большие, тогда как поиск в словарях хорош и быстр.

Мой 2c.