协程是一种用户态的轻量级线程
协程拥有自己的寄存器上下文和栈,能保留上一次调用时的状态。每次过程冲入时,进入上一次离开时所处逻辑流的位置。
协程是串行工作,同一时间只能执行一个携程,不需要加锁。但是进行阻塞(Blocking)操作(如IO操作时)会阻塞掉整个程序。
通过协程在单线程里实现并发效果。
适合做高并发处理。
yield关键字
send()
解决协程阻塞:
greenlet 模块
gevent 模块(基于greenlet模块)
gevent.spawn()启动一个协程
范例:单线程下并发执行一个函数
from urllib.request import urlopen
import gevent
def f(url):
print('GET: %s' %url)
resp = urlopen(url)
data = resp.read()
print('%d byte received form %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/'),
])
结果:
GET: https://www.python.org/
47410 byte received form https://www.python.org/.
GET: https://www.yahoo.com
465372 byte received form https://www.yahoo.com.
GET: https://github.com/
92691 byte received form https://github.com/.
范例:单线程下的多socket并发
server side
import gevent
from gevent import socket,monkey
monkey.patch_all() #monkey 将python中的很多变成非阻塞的形式
def server(port):
s = socket.socket()
s.bind(('0.0.0.0',port))
s.listen(500)
while True:
cli,addr = s.accept()
gevent.spawn(handle_request,cli) #启动了一个协程,将cli客户端穿了进来
def handle_request(s):
try:
while True:
data = s.recv(1024)
print('recv:',data)
s.send(data)
if not data: #没有数据就断开了
s.shutdown(socket.SHUT_WR) #把客户端销毁
except Exception as ex:
print(ex)
finally:
s.close() #把跟这个客户端的链接关掉
if __name__ =='__main__':
server(8001) #启动在8001端口
client side
import socket
HOST = 'localhost'
PORT = 8001
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((HOST,PORT))
while True:
msg = bytes(input('>>:'),encoding='utf8')
s.sendall(msg)
data = s.recv(1024)
print('Reveived',repr(data))
s.close()