Python greenlet与gevent并发编程实战:原理、代码示例及Monkey补丁详解

Ryan
Ryan
发布时间:2026-10-13 | 6人浏览过
原创

本文深入解析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),
  ]
)

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表