协程:
- 定义:微线程。是允许在不同入口点不同位置暂停或开始的计算机程序,简单说,携程就是可以暂停执行的函数。
- 协程原理:记录一个函数的上下文,携程调度切换将记录的上下文保存,在切换回来时进行调取,恢复原有的执行内容,以便从上以此执行位置继续执行。
优点:
- 携程完成多任务占用计算机资源很少
- 由于协程的多任务切换在应用层完成,因此切换开销小
- 协程为单线程序,无需进行共享资源同步互斥处理
缺点:协程的本质是一个单线程,无法利用计算机多核资源
greenlet模块
g=greenlet.green(func)
功能:创建协程对象。 参数:协程函数。
g.switch()
功能:选择要执行的协程函数
示例gl_exer.py:
#!/bin/env python3
import greenlet
def fun1():
print("executing fun1")
gr2.switch() # 跳转到gr2执行
print("finishing func1")
gr2.switch() # 跳转到gr2执行
def fun2():
print("executing fun2")
gr1.switch() # 跳转到gr1执行
print("finishing func2")
# 将函数变成协程
gr1 = greenlet.greenlet(fun1) # 把fun1变成协程
gr2 = greenlet.greenlet(fun2) # 把fun2变成协程
gr1.switch() # 执行协程gr1
执行结果:
[root@gyl-baidu python]# ./gl_exer.py
executing fun1
executing fun2
finishing func1
finishing func2
gevent模块:
gevent.spawn(func,argv) 功能:生产协程对象 参数: func 协程函数 argv 给协程函数传参(不定参) 返回值:协程对象 gevent.joinall(list[,timeout]) 功能:阻塞等待协程只能够完毕 参数: list 协程对象列表 timeout 超时时间 gevent.sleep(sec) 功能:gevent睡眠阻塞 参数:睡眠时间 gevent协程只有遇到gevent指定的阻塞行为时才会自动在协程之间跳转 如果gevent.joinall() gevent.sleep()带来的阻塞
示例代码:gv_exer.py
#!/bin/env python3
import gevent
def add(a, b):
print("Running add ...")
print("%d + %d = %d" % (a, b, a + b))
gevent.sleep(1)
print("Finished add ...")
def sub(a, b):
print("Running sub ...")
print("%d - %d = %d" % (a, b, a - b))
gevent.sleep(2)
print("Finished sub ...")
g1 = gevent.spawn(add, 10, 2)
g2 = gevent.spawn(sub, 10, 2)
# gevent.sleep(1)
gevent.joinall([g1, g2])
执行结果:
[root@gyl-ali python3]# ./gv_exer.py
Running add ...
10 + 2 = 12
Running sub ...
10 - 2 = 8
Finished add ...
Finished sub ...
gevent中monkey脚本
作用:在gevent协程中,协程只有遇到gevent指定类型的阻塞才能跳转到其它协程 因此,将普通的IO阻塞转为可以触发gevent协程的阻塞,以提高执行效率。 转换方法:gevent提供了一个脚本monkey,可以修改底层解释IO阻塞行为, 将很多普通阻塞转变为gevent阻塞。
用法:
- 1、从gevent导入monkey
- 2、 运行相应的脚本,例如,转换socket中所有阻塞 monkey.patch_socket() ,将所有可转换的IO阻塞,全部转换,则运行all monkey.patch_all()
示例代码m_server.py:
#!/bin/env python3
"""
基于协程的TCP并发服务端
"""
import gevent
from gevent import monkey
monkey.patch_all()
import socket
def handle(c):
while True:
data = c.recv(1024).decode()
if not data:
break
print("client:", data)
msg = "server: " + data
c.send(msg.encode())
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(("0.0.0.0", 80))
s.listen(10)
while True:
c, addr = s.accept()
print("connect from ", addr)
gevent.spawn(handle, c)
运行这个脚本并用两个客户端进行连接测试:
[root@gyl-ali python3]# chmod +x m_server.py
[root@gyl-ali python3]# ./m_server.py
connect from ('192.168.1.10', 34958)
client: hello c1
connect from ('192.168.1.20', 57468)
client: hello world