Как проверить, находится ли задача уже в очереди 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.