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