Несколько дней назад я задал вопрос на SO о помощи в разработке парадигмы для структурирования нескольких HTTP-запросов
Вот сценарий. Я хотел бы иметь систему с несколькими производителями, с несколькими потребителями. Мои производители сканируют и очищают несколько сайтов и добавляют найденные ссылки в очередь. Поскольку я буду сканировать несколько сайтов, я хотел бы иметь несколько производителей / сканеров.
Потребители / работники получают данные из этой очереди, отправляют запросы TCP / UDP на эти ссылки и сохраняют результаты в моей базе данных Django. Я также хотел бы иметь несколько рабочих, так как каждый элемент очереди полностью независим друг от друга.
Люди предложили использовать библиотеку сопрограмм для этого, т. Е. Gevent или Eventlet. Никогда не работая с сопрограммами, я прочитал, что, хотя парадигма программирования похожа на многопоточные парадигмы, только один поток активно выполняется, но когда происходят блокирующие вызовы, такие как вызовы ввода-вывода, стеки переключаются в памяти, а другие зеленые поток вступает во владение, пока не встретит какой-то блокирующий вызов ввода / вывода. Надеюсь, я понял это правильно? Вот код из одного из моих SO сообщений:
import gevent
from gevent.queue import *
import time
import random
q = JoinableQueue()
workers = []
producers = []
def do_work(wid, value):
gevent.sleep(random.randint(0,2))
print 'Task', value, 'done', wid
def worker(wid):
while True:
item = q.get()
try:
print "Got item %s" % item
do_work(wid, item)
finally:
print "No more items"
q.task_done()
def producer():
while True:
item = random.randint(1, 11)
if item == 10:
print "Signal Received"
return
else:
print "Added item %s" % item
q.put(item)
for i in range(4):
workers.append(gevent.spawn(worker, random.randint(1, 100000)))
# This doesn't work.
for j in range(2):
producers.append(gevent.spawn(producer))
# Uncommenting this makes this script work.
# producer()
q.join()
Это хорошо работает, потому что sleep
вызовы блокируют вызовы, и когда происходит событие sleep
, другой зеленый поток вступает во владение. Это намного быстрее, чем последовательное выполнение.
Как вы можете видеть, в моей программе нет кода, который бы преднамеренно передавал выполнение одного потока другому потоку. Я не вижу, как это вписывается в приведенный выше сценарий, так как я хотел бы, чтобы все потоки выполнялись одновременно.
Все работает нормально, но я чувствую, что пропускная способность, которую я достиг с помощью Gevent / Eventlets, выше, чем у исходной последовательно запускаемой программы, но значительно ниже, чем то, что можно было бы достичь с помощью реальной многопоточности.
Если бы я заново реализовал свою программу, используя механизмы потоков, каждый из моих производителей и потребителей мог бы одновременно работать без необходимости обмениваться стеками, как сопрограммы.
Должно ли это быть повторно реализовано с использованием потоков? Мой дизайн не так? Мне не удалось увидеть реальные преимущества использования сопрограмм.
Может быть, мои понятия немного грязные, но это то, что я усвоил. Любая помощь или разъяснение моей парадигмы и понятий была бы отличной.
Спасибо