1协程底层–生成器
def producer():
while True:
i = yield 99999
print("第%s个包子做好了"%i)
def consumer():
r=producer()
r.__next__()
count=1
while count<10:
r.send(count)
print("第%s个人吃了第%s个包子"%(count,count))
count+=1
consumer()
2.协程集成手动切换(switch)模块greenlet
"""
greenlet是一个用c实现的协程模块(第三方库),相比于puthon自带的yield,
它可以使你在任意函数之前随意切换,而不需要把这个函数先声明为genertor
"""
from greenlet import greenlet
def t1():
print("1111")
gr2.switch()
def t2():
print("2222")
gr1=greenlet(t1)
gr2=greenlet(t2)
gr1.switch()
"""
手动控制切换,但在有大量未知IO操作时不适用,所以还有一个自动切换模块
"""
3.协程自动切换模块gevent
"""
在一个协程执行中遇到IO阻塞自动切换到另一个协程
"""
import gevent
import requests,time
start=time.time()
def foo(url):
print("Get:%s"%url)
reap=requests.get(url)
date=reap.text
print("%s bytes received from %s"%(len(date),url))
gevent.joinall([
gevent.spawn(foo,"https://www.youkuaiyun.com/"),
gevent.spawn(foo,"https://www.zhipin.com/?ka=header-home"),
gevent.spawn(foo,"https://www.cnblogs.com/linhaifeng/articles/6204014.html")
])
print("cost time:",time.time()-start)
4 阻塞IO和非阻塞IO——server端
"""
阻塞IO,之前写的很多默认的都是阻塞IO,可以通过修改参数等变成非阻塞,
同时要用异常处理,让程序不报错
比如socket里面的accept,recv等
多线程队列里的put,get
"""
import time
import socket
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.bind(('127.0.0.1',6667))
sk.listen(5)
sk.setblocking(False)
try:
print ('waiting client connection .......')
connection,address = sk.accept()
print("+++",address)
client_messge = connection.recv(1024)
print(str(client_messge,'utf8'))
connection.close()
except Exception as e:
print (e)
time.sleep(4)
"""
优点:能够在等待任务完成的时间里干其他活了
(包括提交其他任务,也就是 “后台” 可以有多个任务在同时执行)。
缺点:任务完成的响应延迟增大了,因为每过一段时间才去轮询一次read操作,
而任务可能在两次轮询之间的任意时间完成。这会导致整体数据吞吐量的降低。
"""
5 IO多路复用——cilent端
import socket
sk=socket.socket()
sk.connect(("127.0.0.1",9904))
while 1:
inp=input(">>").strip()
sk.send(inp.encode("utf8"))
data=sk.recv(1024)
print(data.decode("utf8"))
5 IO多路复用——server端
import socket
import select
sk=socket.socket()
sk.bind(("127.0.0.1",9904))
sk.listen(5)
while True:
r,w,e=select.select([sk,],[],[],5)
"""
???在有一个客户端连接的时候,为什么不调用accept,会反复print?
因为select是高水平触发(也就是模电数电里的高电平。其他的还有下降沿,上升沿触发和低电平触发)
我的理解:select是监视内核态里有没有它的监视对象的对应数据,
如果有,它会把这个数据返回给前面的变量,没有则不返回
"""
for i in r:
print("hello")
print('>>>>>>')
6 IO多路复用实现并发–cilent端
import socket
sk=socket.socket()
sk.connect(('127.0.0.1',8801))
while True:
inp=input(">>>>")
sk.sendall(bytes(inp,"utf8"))
data=sk.recv(1024)
print(str(data,'utf8'))
6 IO多路复用实现并发–server端
"""
单线程IO多路复用实现并发,同时监听多个文件描述符
之前第一个是只能允许一个客户端连接,循环聊天
第二个是能允许back_lock个客户端连接,但也只能同时和一个客户端循环聊天,其他客户端被挂起
第三个是借助socketserver来实现多线程并发,同时连接多个并循环聊天
"""
import socket
import select
import time
sk=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.bind(("127.0.0.1",8801))
sk.listen(5)
inputs=[sk,]
while True:
r,w,e=select.select(inputs,[],[],5)
for obj in r:
if obj==sk:
conn,add=obj.accept()
inputs.append(conn)
else:
data_byte=obj.recv(1024)
print(str(data_byte,'utf8'))
inp=input('回答%s号客户>>>'%inputs.index(obj))
obj.sendall(bytes(inp,'utf8'))
time.sleep(20)
print("这里可以做其他事情")
7 封装select的库selectors–cilent端
from socket import *
sk=socket(AF_INET,SOCK_STREAM)
sk.connect(("127.0.0.1",8090))
while True:
inp=input(">>>")
sk.send(inp.encode("utf8"))
date=sk.recv(1024)
print(date.decode("utf8"))
7 封装select的库selectors–server端
import selectors
import socket
sel = selectors.DefaultSelector()
def accept(sock, mask):
conn, addr = sock.accept()
conn.setblocking(False)
sel.register(conn, selectors.EVENT_READ, read)
def read(conn, mask):
try:
data = conn.recv(1024)
conn.send(data)
except Exception:
sel.unregister(conn)
conn.close()
sock = socket.socket()
sock.bind(('localhost', 8090))
sock.listen(100)
sock.setblocking(False)
sel.register(sock, selectors.EVENT_READ, accept)
while True:
events = sel.select()
for key, mask in events:
callback = key.data
callback(key.fileobj, mask)