1.并发、并行、同步、异步、阻塞、非阻塞
并发:是指在同一个时间段内,有几个程序都处于启动运行到运行结束之间
并行:在同一个时间点上,有几个程序同时运行
同步:当一个同步操作发出去后,调用者一直等待返回消息结果,才能进行后续的操作 比如操作文件 打开文件 读取文件 都是同步操作
异步: 当一个异步操作发出去后,调用者不能立刻得到消息结果 创建线程 都是异步操作
阻塞:调用结果返回之前,当前线程会被挂起来,一直处于等待消息通知,不能执行其他业务
非阻塞:调用结果返回之前,该函数不会阻塞当前线程 而立刻返回
2.IO多路复用select、poll、epoll
select
它通过一个select()系统调用来监视多个文件描述符,当select()返回后,该数组中就绪的文件描述符会被内核修改标志位,使进程能够获得这些文件描述符,从 而进行后续的修改
缺点:单个进程能够监视的文件描述符的数量存在最大限制 一般1024
poll
本质跟select()没有区别 但是poll没有限制,使用的是链表存储
epoll
epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描述符数量的 值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符 在系统调用时复制的开销。
另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描, 而poll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描 述符,当进程调用epoll_wait()时便得到通知。
epoll实列
1 import selectors,socket 2 def accept(sock, mask): 3 conn, addr = sock.accept() 4 print('accepted{}from{}'.format(conn,addr)) 5 conn.setblocking(False) 6 sel.register(conn,selectors.EVENT_READ, read) 7 8 def read(conn, mask): 9 data = conn.recv(1024) 10 if data: 11 print('echoing{}to{}'.format(repr(data),conn)) 12 else: 13 print('closing',conn) 14 sel.unregister(conn) 15 conn.close() 16 sel = selectors.DefaultSelector() 17 server = socket.socket() 18 server.bind(('localhost', 9999)) 19 server.listen(500) 20 server.setblocking(False) 21 sel.register(server, selectors.EVENT_READ, accept) #注册事件,只要来一个连接就调accept这个函数, 22 23 while True: 24 events = sel.select()#这个select,看起来是select,有可能调用的是epoll,看你操作系统是Windows的还是Linux的 25 #默认阻塞,有活动连接就返回活动连接列表 26 print('事件',events) 27 28 for key,mask in events: 29 callback = key.data 30 callable(key.fileobj, mask)
客户端
import socket,sys server_address = ('localhost', 9999) # 创建100个 TCP/IP socket实例 socks = [ socket.socket(socket.AF_INET, socket.SOCK_STREAM) for i in range(100)] # 连接服务端 print('connecting to %s port %s' % server_address) for s in socks: s.connect(server_address) s.send(b'kehuduan') data = s.recv(1024) print('服务端回来的数据{}'.format(data)) if not data: print('连接失败') s.close()
3.什么是协成
可以暂停的函数(可以向暂停的地方传入值)
4.生成器
内含yiled关键字的函数
send方法
向生成器发送一个值,随后恢复执行
generator.send(value)
value参数是send方法向生成器发送的值,这个值作为当前yiled表达式的结果
随后生成器恢复执行 直到下一个yiled
def gen_func(): #1 可以产出值 2 可以接受值 yield 333 t = yield 'www.baidu.com' print(t) yield 2 yield 3 yield 4 return 'bobby' if __name__ == '__main__': gen = gen_func() #启动生成器方式两种 nex () send gen.send(None) #gen.close()会触发一次 #gen.throw(Exception,'异常')抛出异常 #send方法可以传递值 进入生成器内部,同时还可以重启生成器执行到下一个yiled位置 next(gen) ret = gen.send('888') print(ret) print(next(gen)) print(next(gen))
thorw方法详解
在生成器暂停的地方抛出类型为type的异常
def gen(): n = 0 while True: try: yield n n += 1 print('n的值{}'.format(n)) except ZeroDivisionError: print('铺货到异常ZerDivisionError') g = gen() ret = next(g) print('第一次的值{}'.format(ret)) ret = g.throw(ZeroDivisionError) print('第二次的值{}'.format(ret)) 之所以第二次 yield 的返回值还是 0,是因为在第一次 yield 的地方抛出了 ZeroDivisionError 异常,而该异常被 except 捕获,跳过了 n += 1 的步骤。 在 except 异常处理器中也可以看到,n 并没有改变,仍然是 0。
没有捕获并处理 throw
传入的异常,异常会回传给调用方。
def gen():
n = 0
while True:
yield n
n += 1
print('n的值{}'.format(n))
g = gen()
ret = next(g)
print('第一次的值{}'.format(ret))
try:
ret = g.throw(ZeroDivisionError)
except ZeroDivisionError:
print('第二次的值{}'.format(ret))
next(g)#对于已经通过抛出异常而退出的生成器再使用 next(g) 会持续抛出 StopIteration 异常。
生成器退出时没有 yield
新值,会抛出 StopIteration
异常。
def gen():
try:
yield 1
except Exception as e:
pass
yield 2
g = gen()
ret = next(g)
print('第一次的值{}'.format(ret))
ret = g.throw(TypeError, '类型错误哟')
# 虽然捕获并处理了 throw 传入的异常,但是由于处理完之后生成器没有后续语句而退出运行,而且并没有 yield 新值,所以会自动抛出一个 StopIteration 异常。
# 如果把 yield 2 注释打开,则不会抛出 StopIteration 异常,因为此时生成器暂停并返回了 2。
close方法
作用:在生成器函数暂停的地方抛出一个 GeneratorExit
异常。
这并不等价于 generator.throw(GeneratorExit),后面会说原因。
如果生成器抛出 StopIteration 异常(不管是由于正常退出还是因为该生成器已经关闭),或者抛出 GeneratorExit 异常(不捕获该异常即 可),close 方法不传递该异常,直接返回到调用方。而生成器抛出的其他异常会传递给调用方。
GeneratorExit 异常的产生意味着生成器对象的生命周期已经结束,因此生成器方法后续语句中不能再有 yield,否则会产生 RuntimeError。(而 throw 方法是期待一个 yield 返回值的,如果没有,则会抛出 StopIteration 异常。)
对于已经正常退出或者因为异常退出的生成器对象,close 方法不会进行任何操作。
第一种情况:不捕获 GeneratorExit
异常,close
方法返回调用方,不传递该异常。
def gen(): yield 1 yield 2 yield 3 g = gen() ret = next(g) print('第一次的值{}'.format(ret)) g.close() next(g)
第二种情况:生成器自然退出抛出 StopIteration
异常,该异常不会传递给调用方,close
方法正常返回。
def gen(): try: yield 1 except GeneratorExit: print('生成器结束') g = gen() ret = next(g) print('第一次的值{}'.format(ret)) g.close()
第三种情况:在 GeneratorExit
抛出后还有 yield
语句,会产生 RuntimeError
。
def gen(): try: yield 1 except GeneratorExit: print('生成器结束') yield 2 g = gen() ret = next(g) print('第一次的值{}'.format(ret)) g.close()
5.yiled from
3.3中新增加的特性
yiled from后面可以是迭代器 可迭代对象 甚至是生成器
#使用yield def gen(*args, **kw): for it in args: for va in it: yield va li = [1,2,3,4] di = {'name':'gaofei','age':'25'} tu = (6,7,8,9) g = gen(li,di,tu) print(next(g))
#使用yield from def gen(*args, **kw): for it in args: yield from it li = [1,2,3,4] di = {'name':'gaofei','age':'25'} tu = (6,7,8,9) g = gen(li,di,tu) print(next(g))
生成器的嵌套
def average_gen(): total = 0 count = 0 average = 0 while True: new_num = yield average count += 1 print(count) total += new_num print(total) average = total/count def proxy_gen(): while True: yield from average_gen() def main(): prox = proxy_gen() print(next(prox)) print(prox.send(10)) print(prox.send(20)) if __name__=="__main__": main()
概念
1.调用方 调用委派生成器的客户端
2.委托生成器 包含yield from表达式生成器函数
作用在子生成器和调用方之间生成建立一个双向通道
调用方可以通过send函数直接发送给子生成器 而子生成器的值也可以直接返回给调用方
3.子生成器 yiled from后面的生成器函数