En corrigeant la réponse de @TemporalBeing ci-dessus, les greenlets ne sont pas "plus rapides" que les threads et c'est une technique de programmation incorrecte de générer 60000 threads pour résoudre un problème de concurrence, un petit pool de threads est plutôt approprié. Voici une comparaison plus raisonnable (de mon post reddit en réponse aux personnes citant ce post SO).
import gevent
from gevent import socket as gsock
import socket as sock
import threading
from datetime import datetime
def timeit(fn, URLS):
t1 = datetime.now()
fn()
t2 = datetime.now()
print(
"%s / %d hostnames, %s seconds" % (
fn.__name__,
len(URLS),
(t2 - t1).total_seconds()
)
)
def run_gevent_without_a_timeout():
ip_numbers = []
def greenlet(domain_name):
ip_numbers.append(gsock.gethostbyname(domain_name))
jobs = [gevent.spawn(greenlet, domain_name) for domain_name in URLS]
gevent.joinall(jobs)
assert len(ip_numbers) == len(URLS)
def run_threads_correctly():
ip_numbers = []
def process():
while queue:
try:
domain_name = queue.pop()
except IndexError:
pass
else:
ip_numbers.append(sock.gethostbyname(domain_name))
threads = [threading.Thread(target=process) for i in range(50)]
queue = list(URLS)
for t in threads:
t.start()
for t in threads:
t.join()
assert len(ip_numbers) == len(URLS)
URLS_base = ['www.google.com', 'www.example.com', 'www.python.org',
'www.yahoo.com', 'www.ubc.ca', 'www.wikipedia.org']
for NUM in (5, 50, 500, 5000, 10000):
URLS = []
for _ in range(NUM):
for url in URLS_base:
URLS.append(url)
print("--------------------")
timeit(run_gevent_without_a_timeout, URLS)
timeit(run_threads_correctly, URLS)
Voici quelques résultats:
--------------------
run_gevent_without_a_timeout / 30 hostnames, 0.044888 seconds
run_threads_correctly / 30 hostnames, 0.019389 seconds
--------------------
run_gevent_without_a_timeout / 300 hostnames, 0.186045 seconds
run_threads_correctly / 300 hostnames, 0.153808 seconds
--------------------
run_gevent_without_a_timeout / 3000 hostnames, 1.834089 seconds
run_threads_correctly / 3000 hostnames, 1.569523 seconds
--------------------
run_gevent_without_a_timeout / 30000 hostnames, 19.030259 seconds
run_threads_correctly / 30000 hostnames, 15.163603 seconds
--------------------
run_gevent_without_a_timeout / 60000 hostnames, 35.770358 seconds
run_threads_correctly / 60000 hostnames, 29.864083 seconds
le malentendu que tout le monde a à propos des E / S non bloquantes avec Python est la croyance que l'interpréteur Python peut s'occuper du travail de récupération des résultats à partir des sockets à grande échelle plus rapidement que les connexions réseau elles-mêmes ne peuvent renvoyer des E / S. Bien que cela soit certainement vrai dans certains cas, ce n'est pas aussi souvent que les gens le pensent, car l'interpréteur Python est vraiment très lent. Dans mon article de blog ici , j'illustre certains profils graphiques qui montrent que même pour des choses très simples, si vous avez affaire à un accès réseau net et rapide à des éléments tels que des bases de données ou des serveurs DNS, ces services peuvent revenir beaucoup plus rapidement que le code Python. peut s'occuper de plusieurs milliers de ces connexions.