Greenlet VS. 主题

我是gevents和greenlets的新手。 我发现了一些关于如何与他们合作的很好的文档,但是没有人给我说明我应该如何以及何时使用greenlet的理由。

  • 他们真的擅长什么?
  • 在代理服务器中使用它们是不是一个好主意?
  • 为什么不是线程?

我不确定的是,如果它们基本上是共同的例程,它们如何为我们提供并发性。

Greenlets提供并发性,但不是并行性。 并发是指代码可以独立于其他代码运行。 并行是同时执行并发代码。 当需要在用户空间中完成很多工作时,并行性特别有用,而这通常是CPU负载较重的工作。 并发性对于分解问题非常有用,可以使不同的部分更加容易并行调度和pipe理。

Greenlets在networking编程中真正的大放异彩,与一个套接字的交互可以独立于与其他套接字的交互而发生。 这是一个并发的典型例子。 因为每个greenlet都在自己的上下文中运行,所以您可以继续使用同步API而无需使用线程。 这很好,因为线程在虚拟内存和内核开销方面非常昂贵,所以使用线程可以实现的并发性显着减less。 另外,由于GIL,Python中的线程比平常更昂贵和更有限。 并发的替代scheme通常是Twisted,libevent,libuv,node.js等项目,其中所有代码共享相同的执行上下文,并注册事件处理程序。

使用greenlet(通过gevent等适当的networking支持)来编写代理是一个很好的主意,因为你对请求的处理能够独立执行,应该这样写。

由于我之前给出的理由,Greenlet提供了并发性。 并发不是并行的。 通过隐藏事件注册并为通常会阻塞当前线程的调用执行调度,像gevent这样的项目公开了这种并发性,而不需要更改asynchronousAPI,而且系统的成本要低得多。

  • 并发不是并行的
  • 线程与进程
  • 多处理与线程
  • GIL与CPython

以@ Max的答案为基础,并添加一些相关的缩放,你可以看到不同之处。 我通过更改要填充的URL来实现这一点,如下所示:

 URLS_base = ['www.google.com', 'www.example.com', 'www.python.org', 'www.yahoo.com', 'www.ubc.ca', 'www.wikipedia.org'] URLS = [] for _ in range(10000): for url in URLS_base: URLS.append(url) 

在我有500个之前,我不得不退出多进程版本。 但是在10,000次迭代中:

 Using gevent it took: 3.756914 ----------- Using multi-threading it took: 15.797028 

所以你可以看到使用gevent在I / O上有一些显着的区别

这是足够有趣的分析。 下面是一个代码来比较greenlet与多处理池与multithreading的性能比较:

 import gevent from gevent import socket as gsock import socket as sock from multiprocessing import Pool from threading import Thread from datetime import datetime class IpGetter(Thread): def __init__(self, domain): Thread.__init__(self) self.domain = domain def run(self): self.ip = sock.gethostbyname(self.domain) if __name__ == "__main__": URLS = ['www.google.com', 'www.example.com', 'www.python.org', 'www.yahoo.com', 'www.ubc.ca', 'www.wikipedia.org'] t1 = datetime.now() jobs = [gevent.spawn(gsock.gethostbyname, url) for url in URLS] gevent.joinall(jobs, timeout=2) t2 = datetime.now() print "Using gevent it took: %s" % (t2-t1).total_seconds() print "-----------" t1 = datetime.now() pool = Pool(len(URLS)) results = pool.map(sock.gethostbyname, URLS) t2 = datetime.now() pool.close() print "Using multiprocessing it took: %s" % (t2-t1).total_seconds() print "-----------" t1 = datetime.now() threads = [] for url in URLS: t = IpGetter(url) t.start() threads.append(t) for t in threads: t.join() t2 = datetime.now() print "Using multi-threading it took: %s" % (t2-t1).total_seconds() 

这里是结果:

 Using gevent it took: 0.083758 ----------- Using multiprocessing it took: 0.023633 ----------- Using multi-threading it took: 0.008327 

我认为greenlet声称它不像GIL一样被GIL绑定。 此外,格林莱特博士说,这是用于networking运营。 对于networking密集型操作来说,线程切换是很好的,你可以看到multithreading方法非常快。 另外使用python的官方库总是可以的。 我尝试在Windows上安装greenlet,并遇到一个DLL依赖问题,所以我在linux虚拟机上运行这个testing。 一直试图编写一个代码,希望它可以在任何机器上运行。