การแก้ไขคำตอบของ @TemporalBeing ด้านบนกรีนเล็ตไม่ได้ "เร็ว" กว่าเธรดและเป็นเทคนิคการเขียนโปรแกรมที่ไม่ถูกต้องในการวางเธรด 60000 เธรดเพื่อแก้ไขปัญหาการทำงานพร้อมกัน นี่คือการเปรียบเทียบที่สมเหตุสมผลมากขึ้น (จากโพสต์ redditของฉันเพื่อตอบสนองต่อคนที่อ้างถึงโพสต์ 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)
นี่คือผลลัพธ์บางส่วน:
--------------------
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
ความเข้าใจผิดที่ทุกคนมีเกี่ยวกับการไม่ปิดกั้น IO ด้วย Python คือความเชื่อที่ว่า Python interpreter สามารถเข้าร่วมในการดึงผลลัพธ์จากซ็อกเก็ตในขนาดใหญ่ได้เร็วกว่าการเชื่อมต่อเครือข่าย แม้ว่านี่จะเป็นจริงในบางกรณี แต่ก็ไม่เป็นความจริงอย่างที่คนทั่วไปคิดเพราะล่าม Python ช้าจริงๆ ในบล็อกโพสต์ของฉันที่นี่ฉันแสดงโปรไฟล์กราฟิกบางอย่างที่แสดงว่าสำหรับสิ่งที่ง่ายมากถ้าคุณจัดการกับเครือข่ายที่คมชัดและรวดเร็วในการเข้าถึงสิ่งต่าง ๆ เช่นฐานข้อมูลหรือเซิร์ฟเวอร์ DNS บริการเหล่านั้นสามารถกลับมาเร็วกว่ารหัส Python สามารถเข้าร่วมหลายพันคนของการเชื่อมต่อเหล่านั้น