python协程和异步IO

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)
View Code

  客户端

    

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))
View Code

  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。
View Code

  没有捕获并处理 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)
View Code

  第二种情况:生成器自然退出抛出 StopIteration 异常,该异常不会传递给调用方,close 方法正常返回。

def gen():
    try:
        yield 1
    except GeneratorExit:
        print('生成器结束')

g = gen()
ret = next(g)
print('第一次的值{}'.format(ret))
g.close()
View Code

  第三种情况:在 GeneratorExit 抛出后还有 yield 语句,会产生 RuntimeError

def gen():
    try:
        yield 1
    except GeneratorExit:
        print('生成器结束')
        yield 2
g = gen()
ret = next(g)
print('第一次的值{}'.format(ret))
g.close()
View Code

 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))
View Code
#使用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))
View Code

  生成器的嵌套

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()
View Code

  概念

    1.调用方 调用委派生成器的客户端

    2.委托生成器 包含yield from表达式生成器函数

      作用在子生成器和调用方之间生成建立一个双向通道

      调用方可以通过send函数直接发送给子生成器 而子生成器的值也可以直接返回给调用方

    3.子生成器 yiled from后面的生成器函数

    

  

  

转载于:https://www.cnblogs.com/gaosie/p/10829065.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值