应用
猴子补丁
把标准库中的thread/socket等给替换掉,变为非阻塞。
import gevent
gevent.monkey.patch_all()
# 只给Socket打补丁
gevent.monkey.patch_socket()
# 不打补丁,引入gevent内置非阻塞socket。
from gevent import socket
组合池(Group & Pool)
Group是用来控制先后顺序,Pool是控制最大并发执行数。
Group
Pool
from gevent.pool import Pool
pool = Pool(2)
def foo(i):
print('id:%s, pool_size:%s', i, len(pool))
pool.map(foo, range(4))
#输出
id:%s, pool_size:%s 0 2
id:%s, pool_size:%s 1 2
id:%s, pool_size:%s 2 1
超时限制
from gevent import Timeout
sec = 4
timeout = Timeout(sec)
timeout.start()
def wait():
gevent.sleep(sec)
gevent.spawn(wait).join()
import gevent
from gevent import Timeout
sec = 4
class TimeOutEx(Exception):
pass
with Timeout(sec, TimeOutEx):
gevent.sleep(sec)
gevent.joinall(jobs, timeout=2)
ThreadPool
实际是创建多进程执行。有CPU密集任务使用它。
http://xiaorui.cc/?p=1530
from gevent.threadpool import ThreadPool
from gevent import get_hub
tp = get_hub().threadpool
tp.apply()
锁和信号量
Lock & RLock
Reentrant Lock同一个线程可以多次获取,防止线程自己导致自己死锁。
Semaphore(信号量)
acquire会消耗一个信号量,如果降为0则阻塞其他acquire调用,起到阻塞其他协程的作用。
原理
重要类
Greenlet
gevent协程类
import gevent
from gevent import Greenlet
class FooGreenlet(Greenlet):
def __init__(self, message, n):
Greenlet.__init__(self)
self.message = message
self.n = n
def _run(self):
print(self.message)
gevent.sleep(self.n)
g = FooGreenlet("Hi there!", 3)
g.start()
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