如何高效地遍历多个生成器?

3
我有三个不同的生成器,可以从网络中提取数据。因此,每次迭代可能需要一些时间才能完成。
我希望混合调用这些生成器,考虑使用roundrobin(在这里找到)。 问题是每个调用都会被阻塞,直到它完成为止。
是否有一种方法可以同时循环遍历所有生成器,而不会阻塞?

我有一个模块,可以使用线程合并您的生成器,添加一个生成器/迭代输出只需要一秒钟。 - lunixbochs
使用线程吗?也许可以,但GIL可能会破坏它。 - Joran Beasley
线程非常适合处理奇怪的阻塞IO - 它们会释放GIL,直到远程IO返回。 - lunixbochs
2个回答

5
您可以使用我的 ThreadPool 类的 iter() 方法来实现此操作。 pool.iter() 会一直产生线程函数返回值,直到所有被装饰和调用的函数执行完毕。请对您的所有异步函数进行装饰,然后调用它们,最后通过循环 pool.iter() 来捕获返回值。
例如:
import time
from threadpool import ThreadPool
pool = ThreadPool(max_threads=25, catch_returns=True)

# decorate any functions you need to aggregate
# if you're pulling a function from an outside source
# you can still say 'func = pool(func)' or 'pool(func)()
@pool
def data(ID, start):
    for i in xrange(start, start+4):
        yield ID, i
        time.sleep(1)

# each of these calls will spawn a thread and return immediately
# make sure you do either pool.finish() or pool.iter()
# otherwise your program will exit before the threads finish
data("generator 1", 5)
data("generator 2", 10)
data("generator 3", 64)

for value in pool.iter():
    # this will print the generators' return values as they yield
    print value

这太棒了。在代码中切片生成器是否可行?因为即使我切片pool.iter()生成器,线程仍然会产生所有数据,而我的生成器非常大(5000+),而我只需要20个结果。 - iTayb
似乎itertools.islice没有被识别为生成器。此外,在每个self.returned调用之间需要一点睡眠,因为如果它们返回得非常快,其中一些将会丢失。这是我对threadpool.ThreadPool.worker的try块进行修复的版本:http://pastebin.com/inTxx2tB - iTayb
谢谢啊!我可以再请你帮忙做最后一个修改吗?我需要一种方式来终止线程池,并要求所有线程立即停止任务?self.finish() 可以实现,但是它会阻塞。 - iTayb
@lunixbochs:您能否在您的meta/snippets目录下加上许可证?MIT/BSD将不胜感激。 - j13r
我刚刚将它授权为MIT。 - lunixbochs
显示剩余3条评论

1
简而言之,没有好的方法可以在没有线程的情况下完成这个任务。
有时ORM会增加一些查看函数或回调函数来指示数据何时可用。否则,您需要生成线程才能完成此操作。如果线程不是一个选项,您可以尝试将数据库库替换为异步库。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接