Python greenlet与gevent并发编程实战:原理、代码示例及Monkey补丁详解
本文深入解析Python中greenlet微线程与gevent协程库的使用。通过具体代码示例,展示如何利用greenlet进行手动线程切换,以及如何基于greenlet构建gevent实现IO操作自动切换。重点介绍gevent的monkey.patch_all()机制对系统IO的自动修补,涵盖网络请求、Socket通信及队列处理中的强制切换技巧,帮助开发者掌握Python高并发场景下的最佳实践。
greenlet微线程基础与手动切换示例
greenlet示例
greenlet提供了一种微线程机制,允许开发者在单个线程内手动控制执行流的切换。
示例1:线程切换
from greenlet import greenlet
def test1(x,y):
z = gr2.switch(x+y)
print(z)
def test2(u):
print(u)
gr1.switch(42)
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch("hello",'world')
代码中定义了gr1和gr2两个greenlet实例。首先调用gr1.switch("hello", 'world')启动gr1,gr1执行test1函数。在test1内部,通过gr2.switch(x+y)将控制权切换至gr2,此时gr2执行test2并打印参数"helloworld"。随后,test2通过gr1.switch(42)将控制权交还gr1,并传递返回值42。最终在gr1中获取该返回值并打印。
上述过程的返回值被赋给变量z,值为42,随后被打印输出。
具体的执行顺序如下:
gr1.switch("hello", 'world') 触发 test1('hello', 'world') 执行
在test1中调用 gr2.switch('helloworld') 切换到 test2('helloworld'),执行 print('helloworld')
test2中调用 gr1.switch(42) 切换回 gr1,此时 z 被赋值为 42,随后执行 print(42)
最终打印结果如下:
helloworld 42
示例2
from greenlet import greenlet
def eat(name):
print('%s eat 1' %name)
g2.switch('egon')
print('%s eat 2' %name)
g2.switch()
def play(name):
print('%s play 1' %name)
g1.switch()
print('%s play 2' %name)
g1=greenlet(eat)
g2=greenlet(play)
g1.switch('egon')#可以在第一次switch时传入参数,以后都不需要
在第一次调用g1.switch('egon')时传入参数,后续切换时无需再传参。
gevent协程库原理与IO自动切换
gevent
gevent基于greenlet构建,其核心特性是在遇到IO操作时自动进行切换。常见的IO操作包括网络请求等。此外,也可以通过调用gevent.sleep(0)来强制进行上下文切换,即使没有实际IO阻塞。
示例1
import gevent
def func1():
print("start func1")
gevent.sleep(1)
print("end func1")
def func2():
print("start func2")
gevent.sleep(1)
print("end func2")
gevent.joinall(
[
gevent.spawn(func1),
gevent.spawn(func2)
]
)
执行结果如下:
start func1 start func2 end func1 end func2
Monkey补丁机制与系统IO修补
示例2:gevent使用monkey对所有系统自带的IO操作打patch
```python
from gevent import monkey;monkey.patch_all()
import gevent
import time
def eat():
print('eat food 1')
time.sleep(2) # 会自动的跳转到play
print('eat food 2')
def play():
print('play 1')
time.sleep(1) # 会自动的跳转到eat
print('play 2')
g1=gevent.spawn(eat)
g2=gevent.spawn(play)
gevent.joinall([g1,g2])
print('end')
执行结果如下:
eat food 1 play 1 play 2 eat food 2 end
gevent网络请求与Socket应用示例
示例3:发送请求
from gevent import monkey; monkey.patch_all()
import gevent
import requests
def f(url):
print('GET: %s' % url)
resp = requests.get(url)
data = resp.text
print('%d bytes received from %s.' % (len(data), url))
gevent.joinall([
gevent.spawn(f, 'https://www.python.org/'),
gevent.spawn(f, 'https://www.yahoo.com/'),
gevent.spawn(f, 'https://github.com/'),
gevent.spawn(f, 'https://github.com/'),
gevent.spawn(f, 'https://github.com/'),
gevent.spawn(f, 'https://github.com/'),
gevent.spawn(f, 'https://github.com/'),
])
示例4:使用gevent的socket替代系统的socket
import gevent
from gevent import socket
urls = ['www.baidu.com', 'www.163.com', 'www.qq.com']
jobs = [gevent.spawn(socket.gethostbyname, url) for url in urls]
gevent.joinall(jobs, timeout=2)
print([job.value for job in jobs])
或使用patch_socket()
from gevent import monkey; monkey.patch_socket()
import gevent
def f(n):
for i in range(n):
print(gevent.getcurrent(), i)
gevent.sleep(0) # 不加的话不会交替执行
g1 = gevent.spawn(f, 5)
g2 = gevent.spawn(f, 5)
g3 = gevent.spawn(f, 5)
g1.join()
g2.join()
g3.join()
队列处理中的强制切换技巧
示例5:在队列中使用gevent.sleep(0)强制切换到其他线程
import gevent
from gevent.queue import Queue
def func():
for i in range(10):
print("int the func")
q.put(f"test{i}")
gevent.sleep(0)
def func2():
for i in range(10):
print("int the func2")
res = q.get()
print("--->",res)
q = Queue()
gevent.joinall(
[
gevent.spawn(func2),
gevent.spawn(func),
]
)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













