几天前,我问了一个关于 SO 的问题,关于帮助我设计一个用于构建多个 HTTP 请求的范例
这是场景。我想要一个多生产者、多消费者的系统。我的生产者抓取并抓取了一些网站,并将找到的链接添加到队列中。由于我将爬取多个站点,因此我希望有多个生产者/爬虫。
消费者/工作人员从这个队列中获取信息,向这些链接发出 TCP/UDP 请求,并将结果保存到我的 Django DB 中。我还希望有多个工作人员,因为每个队列项目彼此完全独立。
人们建议为此使用协程库,即 Gevent 或 Eventlet。从未使用过协程,我读到尽管编程范式类似于线程范式,但只有一个线程正在积极执行,但是当阻塞调用发生时 - 例如 I/O 调用 - 堆栈在内存中切换,另一个绿色线程接管,直到遇到某种阻塞 I/O 调用。希望我做对了吗?这是我的一篇 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 实现的吞吐量高于原始的顺序运行程序,但远低于使用真实线程可以实现的吞吐量。
如果我要使用线程机制重新实现我的程序,我的每个生产者和消费者都可以同时工作,而无需像协程那样交换堆栈。
是否应该使用线程重新实现?我的设计错了吗?我没有看到使用协程的真正好处。
也许我的概念有点混乱,但这就是我所吸收的。对我的范式和概念的任何帮助或澄清都会很棒。
谢谢