1.协程
并发:切+保存状态
单线程下实现并发:协程 切+ 保存状态 yield
遇到io切,提高效率
遇到计算切,并没有提高效率
检测单线程下 IO行为 io阻塞 切
相当于骗操作系统 一直处于计算
协程:。。。
单线程下实现并发:根本目标:遇到IO就切,一个线程的整体IO降下来
程序用的cpu 时间长,就叫执行效率高
效率最高:多个进程 (多个cpu)
每个进程开多个线程
每个线程用到协程 (IO就切)
总结协程特点:
#并发执行
import time
def producer():
g=consumer()
next(g)
for i in range(10000000): # 计算
g.send(i)
def consumer():
while True:
res=yield
start_time=time.time()
producer()
stop_time=time.time()
print(stop_time-start_time)
#串行
import time
def producer():
res=[]
for i in range(10000000):
res.append(i)
return res
def consumer(res):
pass
start_time=time.time()
res=producer()
consumer(res)
stop_time=time.time()
print(stop_time-start_time)
2.greenlet模块
pip3 install greenlet
greenlet:可以很方便的切 但不能检测到 遇到IO 切
greenlet 比yield 好 但是还是不好 遇到io不会切
from greenlet import greenlet
import time
def eat(name):
print('%s eat 1' %name)
time.sleep(10) # 遇到io 不会立即切
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') # 第一次切 需要传参数
3.gevent模块
pip3 install gevent
gevent:封装了greenlet模块,但是他能检测到io 自动切
只能检测到gevent.sleep() gevent的IO阻塞
加上补丁后,就可以检测到所有的IO 原理是:将阻塞变为非阻塞
from gevent import monkey;monkey.patch_all()
这种形式的协程 才能帮我们提升效率 从始至终 就一个线程
gevent.joinall([g1,g2]) 等待全部执行完
gevent 模块:监测单线程下多个任务得IO行为实现遇到IO就自动切到另一个任务去执行
提升单线程运行效率
应用场景:单线程下多个任务io密集型
ftp io密集型 线程来回切 比os q切 小路高
from gevent import monkey;monkey.patch_all() # 一定要放在程序的开头 检测所以的io 将阻塞变成非阻塞
import gevent
import time
def eat(name):
print('%s eat 1' % name)
time.sleep(3) # 7s多一些 gevent 只识别 gevent 的 io操作
# gevent.sleep(3) # 4s 多一些
print('%s eat 2' % name)
def play(name):
print('%s play 1' % name)
time.sleep(4)
# gevent.sleep(4)
print('%s play 2' % name)
start_time=time.time()
g1=gevent.spawn(eat,'egon')
g2=gevent.spawn(play,'alex')
g1.join()
g2.join()
stop_time=time.time()
print(stop_time-start_time)
"""
egon eat 1
alex play 1
egon eat 2
alex play 2
4.001747369766235
"""
"""
egon eat 1
egon eat 2
alex play 1
alex play 2
7.0017828941345215
"""
"""
egon eat 1
alex play 1
egon eat 2
alex play 2
4.001675367355347
"""
from gevent import monkey;monkey.patch_all()
import gevent
import time
def eat(name):
print('%s eat 1' % name)
time.sleep(3)
print('%s eat 2' % name)
def play(name):
print('%s play 1' % name)
time.sleep(4)
print('%s play 2' % name)
g1=gevent.spawn(eat,'egon') # 异步操作
g2=gevent.spawn(play,'alex')
# time.sleep(5) # 得等到 全部执行完
# g1.join() # 等到 全部执行完
# g2.join()
gevent.joinall([g1,g2]) # 等到g1 g2 全部执行完
"""
egon eat 1
alex play 1
egon eat 2
alex play 2
"""
4.gevent实现并发的套接字通信
# 500 客户端同时 登录 服务端:这里1个线程 抗住了 500个client
# 这里也说明了:单线程下面io问题降下来,效率大幅度提高
说明
使用:多进程
多线程
一个线程io 问题解决了 效率大大得提高
服务端:
#基于gevent实现
from gevent import monkey,spawn;monkey.patch_all()
from socket import *
def communicate(conn):
while True:
try:
data=conn.recv(1024)
if not data:break
conn.send(data.upper())
except ConnectionResetError:
break
conn.close()
def server(ip,port):
server = socket(AF_INET, SOCK_STREAM)
server.bind((ip,port))
server.listen(5)
while True:
conn, addr = server.accept()
spawn(communicate,conn) # 这里没必要加join
server.close()
if __name__ == '__main__':
g=spawn(server,'127.0.0.1',8090)
g.join()
客户端:
from socket import *
from threading import Thread,currentThread
def client(): #
client=socket(AF_INET,SOCK_STREAM)
client.connect(('127.0.0.1',8090))
while True:
client.send(('%s hello' %currentThread().getName()).encode('utf-8'))
data=client.recv(1024)
print(data.decode('utf-8'))
client.close()
# 500 客户端同时 登录 服务端:这里1个线程 抗住了 500个client
# 这里也说明了:单线程下面io问题降下来,效率大幅度提高
if __name__ == '__main__':
for i in range(500): # 500 客户端同时 登录 服务端:这里1个线程 抗住了 500个client
t=Thread(target=client)
t.start()