本文从应用和原理两部分介绍gevent。

应用

猴子补丁

把标准库中的thread/socket等给替换掉,变为非阻塞。

  1. import gevent
  2. gevent.monkey.patch_all()
  3. # 只给Socket打补丁
  4. gevent.monkey.patch_socket()
  5. # 不打补丁,引入gevent内置非阻塞socket。
  6. from gevent import socket

组合池(Group & Pool)

Group是用来控制先后顺序,Pool是控制最大并发执行数。

Group

Pool

  1. from gevent.pool import Pool
  2. pool = Pool(2)
  3. def foo(i):
  4. print('id:%s, pool_size:%s', i, len(pool))
  5. pool.map(foo, range(4))
  6. #输出
  7. id:%s, pool_size:%s 0 2
  8. id:%s, pool_size:%s 1 2
  9. id:%s, pool_size:%s 2 1

超时限制

  1. from gevent import Timeout
  2. sec = 4
  3. timeout = Timeout(sec)
  4. timeout.start()
  5. def wait():
  6. gevent.sleep(sec)
  7. gevent.spawn(wait).join()
  1. import gevent
  2. from gevent import Timeout
  3. sec = 4
  4. class TimeOutEx(Exception):
  5. pass
  6. with Timeout(sec, TimeOutEx):
  7. gevent.sleep(sec)
  1. gevent.joinall(jobs, timeout=2)

ThreadPool

实际是创建多进程执行。有CPU密集任务使用它。
http://xiaorui.cc/?p=1530

  1. from gevent.threadpool import ThreadPool
  2. from gevent import get_hub
  3. tp = get_hub().threadpool
  4. tp.apply()

锁和信号量

Lock & RLock

Reentrant Lock同一个线程可以多次获取,防止线程自己导致自己死锁。

Semaphore(信号量)

acquire会消耗一个信号量,如果降为0则阻塞其他acquire调用,起到阻塞其他协程的作用。

原理

基于libev和libuv的基于C语言实现的事件轮询。

重要类

Greenlet

gevent协程类

  1. import gevent
  2. from gevent import Greenlet
  3. class FooGreenlet(Greenlet):
  4. def __init__(self, message, n):
  5. Greenlet.__init__(self)
  6. self.message = message
  7. self.n = n
  8. def _run(self):
  9. print(self.message)
  10. gevent.sleep(self.n)
  11. g = FooGreenlet("Hi there!", 3)
  12. g.start()
  13. g.join()

Greenlet.switch()调用会切换到Greenlet.run()方法运行。所以子类要实现自定义的run()。
Waiter类
Waiter.switch会调用所属greenlet.switch,从而切到指定协程。
greenlet.get()会阻塞获取返回值,类似gevent.joinall()

Hub类

gevent hub 是 greenlet子类是main greenlet,切换主greenlet后先调用hub.wait,然后再greenlet.so中执行真的切换
普通greenlet中 socket封装到libev的io watcher,watcher与此greenlet关联。
libev loop检测到IO事件,将切换到与之对应的greenlet。
使用libev调度所有greenlet,libev里有个loop

全部通过Hub.wait(watcher) -> Hub.switch切换到hub主协程,事件触发再回调Watcher.switch() - > greenlet.switch()切换回此协程。

self.hub.wait(self._read_event)

Waiter类

Waiter类有greenlet代表属于哪个协程,Waiter注册到循环中,等待触发回调。
原理图
MainThread就是hub
image.png

http://blog.psjay.com/posts/logging_gevent_and_deadlock/