Различия в пропускной способности при использовании сопрограмм и потоковой передачи

Несколько дней назад я задал вопрос о 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, выше, чем у исходной последовательно выполняемой программы, но значительно ниже, чем та, которую можно было бы достичь с использованием реальной потоковой передачи.

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

Следует ли повторно реализовать это с использованием потоковой передачи? Мой дизайн неправильный? Я не смог увидеть реальных преимуществ использования сопрограмм.

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

Спасибо

5
задан Delimitry 15 December 2017 в 23:17
поделиться