使用gevent模块实现并发
例子
服务器 from gevent import monkey#往常扩展模块要在内置模块下面,但是这个扩展模块必须写下上面 monkey.patch_all() import gevent import socket def talk(): while True: conn.send(b'helle') msg = conn.recv(1024) print(msg) conn.close() sk=socket.socket() sk.bind(('127.0.0.1',9090)) sk.listen() while True: conn,addr = sk.accept() gevent.spawn(talk,conn)#实现并发 sk.close() #===================== 客户端 import socket sk = socket.socket() sk.connect(('127.0.0.1',9000)) while True: msg=sk.recv(1024) print(msg) ret = input('>>>').encode('utf-8') sk.send(ret) sk.close()
协程
# 协程和线程有什么关系? #协程是线程内的一个单位 #线程已经是CPU调度的最小单位了 #协程--又叫纤程 #Cpython解释器下的多线程,在同一时刻只能有一个线程访问CPU #所以多线程能发挥显著作用的时候就是不太耗CPU,涉及到的计算操作少 #IO高,大部分时间都用再等待上 #协程的本质是一条线程的一部分---下图是解释什么是协程,就是相当于是并发的样子,两个线程
#线程: #正常的线程,遇到阻塞就停下来,知道阻塞事件结束,才继续 # 协程: #利用了协程,就把线程分成了好几段,在一个任务出现阻塞的时候,自动的切换到 #另一个任务去执行,这些所有的事在一个线程里面完成。 #如果这个程序从头到尾没有IO,没有阻塞,程序根本就不会在多个任务之间切换 #就是一个顺序执行的,协程对于高计算型的代码没有用 #对于高IO型 #线程 #是计算机中最小的CPU调度单位 #协程 # --不用考虑数据安全,因为就在一个线程上执行 # --协程是如何调度的? #协程不能被操作系统调度 #是用户级别来调度的,就是gevent调度的 #协程的调度速度比线程还快 #协程的调度由于是Python代码级别的而不是操作系统级别的,所以用户的可控制性更强,降低了操作系统的工作量
协程到底是怎么实现程序之间的切换的
#生成器 def func(): #func是一个任务 print(123) yield 1 print(456) yield 2 print(789) yield 3 g =func() print(g.__next__())#程序之间的切换,代码会记住执行的位置,等待下一次执行 def wahaha(g):#wahaha是一个任务 for i in g: print(i) g = func() wahaha(g) #两个任务交替执行---说明程序之间的切换是可以控制的
import greenlet
#两个模块gevent,greenLet
#gevent是依赖greenLet 协程之间的切换是由greenLet完成的
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时传入参数,以后都不需要
#程序级别的切换
#切换并不能让你的程序效率提高,反而会下降
#如果遇到IO才切换,那么就能提高效率,这个IO切换是由gevent借助greenLet来实现的
import gevent#能够在遇到自己认识的io操作的时候,主动调用greenLet中的switch来切换到其他的任务已提高程序的效率 def eat(name): print('%s eat 1' %name) gevent.sleep(2) print('%s eat 2' %name) def play(name): print('%s play 1' %name) gevent.sleep(1) print('%s play 2' %name) g1=gevent.spawn(eat,'egon') g2=gevent.spawn(play,name='egon') g1.join() g2.join() #或者gevent.joinall([g1,g2]) print('主')
如果吧.join()取消,那么只会打印出‘主’
import gevent#能够在遇到自己认识的io操作的时候,主动调用greenLet中的switch来切换到其他的任务已提高程序的效率 def eat(name): print('%s eat 1' %name) gevent.sleep(2) print('%s eat 2' %name) def play(name): print('%s play 1' %name) gevent.sleep(1) print('%s play 2' %name) g1=gevent.spawn(eat,'egon') #就是发布了任务 g2=gevent.spawn(play,name='egon')#就是发布了任务 gevent.sleep(1) #使用这个睡1S,能够打印出部分 #g1.join() #等待g1任务执行完毕,阻塞,帮助你完成g1中函数的全部内容 #g2.join() #或者gevent.joinall([g1,g2]) #这个可以实现上面两个.join()的功能 print('主')
gevent并不认识不在这个模块中的IO操作time.sleep(2)
import time import gevent def eat(name): print('%s eat 1' %name) time.sleep(2) print('%s eat 2' %name) def play(name): print('%s play 1' %name) time.sleep(1) print('%s play 2' %name) g1=gevent.spawn(eat,'egon') g2=gevent.spawn(play,name='egon') g1.join() g2.join() #或者gevent.joinall([g1,g2]) print('主')
解决办法
from gevent import monkey monkey.patch_all()#这个作用就是将gevent模块之外的sleep归为我认识的IO操作,就能认识time模块中的sleep了 import time import gevent def eat(name): print('%s eat 1' %name) time.sleep(2) print('%s eat 2' %name) def play(name): print('%s play 1' %name) time.sleep(1) print('%s play 2' %name) g1=gevent.spawn(eat,'egon') g2=gevent.spawn(play,name='egon') g1.join() g2.join() #或者gevent.joinall([g1,g2]) print('主')
验证确定是否是协程
from gevent import monkey monkey.patch_all()#这个作用就是将gevent模块之外的sleep归为我认识的IO操作,就能认识time模块中的sleep了 import time import gevent from threading import currentThread def eat(name): print(currentThread()) print('%s eat 1' %name) time.sleep(2) print('%s eat 2' %name) def play(name): print(currentThread()) print('%s play 1' %name) time.sleep(1) print('%s play 2' %name) g1=gevent.spawn(eat,'egon') g2=gevent.spawn(play,name='egon') g1.join() g2.join() #或者gevent.joinall([g1,g2]) print('主')
如何解决并发问题
#解决并发问题?? #多进程 高计算型,浪费操作系统和资源,可以利用多核 #多线程 高IO型,也会给操作系统添加负担,会占用比进程少的资源 #受到GIL的影响 # 不能利用多核 #协程 高IO型,完全不会给操作系统添加负担,几乎不占资源 #始终不能利用多核 #多进程+多线程 :建议不超过100条 #多进程+多线程+协程 :最多接收50000条,这个是多快的 #假设是4*CPU #多进程 CPU+1=5 #多线程 CPU*5 = 20 #协程 500个协程
验证协程是否能执行500个请求---使用协程接受500个并发
#服务器 from gevent import monkey#往常扩展模块要在内置模块下面,但是这个扩展模块必须写下上面 monkey.patch_all() import gevent import socket def talk(): while True: conn.send(b'helle') msg = conn.recv(1024) print(msg) conn.close() sk=socket.socket() sk.bind(('127.0.0.1',9090)) sk.listen() while True: conn,addr = sk.accept() gevent.spawn(talk,conn)#实现并发 sk.close() #客户端 import socket from threading import Thread def client(): sk = socket.socket() sk.connect(('127.0.0.1',9000)) while True: msg=sk.recv(1024) print(msg) ret = input('>>>').encode('utf-8') sk.send(ret) sk.close() if __name__ == '__main__': for i in range(500): Thread(target=client).start()