zoukankan      html  css  js  c++  java
  • python协程详解,gevent asyncio

    python协程详解,gevent asyncio

    #协程的概念
    #模块操作协程
    # gevent 扩展模块
    # asyncio 内置模块
    # 基础的语法


    1、生成器实现切换

    [1]

    import time
    def func1():
        print(1)
        yield 1
        time.sleep(1)
        print(2)
    def func2():
        g=func1()
        next(g)
    func2()
    ------------结果:
    1

    [2]

    import time
    def func1():
        print(1)
        yield 1
        time.sleep(1)
        print(2)
        yield 
    def func2():
        g=func1()
        next(g)
        print('func2')
        next(g)
    func2()
    ------------结果:
    1
    func2
    2

    #注释:执行函数2的过程中实现了两次切换,但是阻塞sleep没有规避掉

    [3]生成器报错现象

    import time
    def func1():
        print(1)
        yield 1
        time.sleep(1)
        print(2)
    g=func1()
    next(g)
    print('func2')
    next(g)
    ---------结果:
    1
    func2
    2
    ......
        next(g)
    StopIteration

    #注释:跟2相比,执行生成器最后的print(2),由于后面没有yield了,虽然语句执行了但是会报错。所有生成器最后应该是yield,这样取最后的值才不会出现报错。超出迭代的数了就会报错,一个yield一次迭代。
    [4]小结:
    #协程 :能够在一个线程下的多个任务之间来回切换,那么每一个任务都是一个协程
    实现一个线程多个个任务的切换就是协程了,但是只是yield还不能规避io实现协程切换。
    原生python的asyncio就是靠yield实现协程的

    2、 C语言完成的python模块 greenlet gevent

    Switch在一些计算机语言中是保留字,其作用大多情况下是进行判断选择。以C语言来说,switch(开关语句)常和case break default一起使用。

    [1]用greenlet执行一个函数

    #实例化一个greenlet(eat)对象,将函数传进去,对象.开关()执行这个函数

    import time
    from  greenlet import greenlet
    
    def eat():
        print('魔降风云变 is eating')
        time.sleep(0.5)
        print('魔降风云变 finished eat')
    
    def sleep():
        print('小马过河 is sleeping')
        time.sleep(0.5)
        print('小马过河 finished sleep')
    g1 = greenlet(eat)
    g1.switch()
    -----------结果:
    魔降风云变 is eating
    魔降风云变 finished eat

    [2]在一个任务函数中切换到另一个任务函数去执行,然后没有再切换回来

    #g1.switch()执行g1的任务函数,g1的任务函数中遇到g2.switch()就去执行g2的任务函数。此时g2执行完了,但是g1没有继续往下执行了。

    import time
    from  greenlet import greenlet
    def eat():
        print('魔降风云变 is eating')
        g2.switch()
        time.sleep(0.5)
        print('魔降风云变 finished eat')
    
    def sleep():
        print('小马过河 is sleeping')
        time.sleep(0.5)
        print('小马过河 finished sleep')
    g1 = greenlet(eat)
    g2 = greenlet(sleep)
    g1.switch()
    --------------结果:
    魔降风云变 is eating
    小马过河 is sleeping
    小马过河 finished sleep

    [3]一个任务中切换到另一个任务,另一个任务执行完了再执行切换回到这个任务执行。实现两个任务间切换并都执行结束

    #g1.switch()执行g1任务函数eat,eat中遇到g2.switch()执行g2任务函数sleep,sleep执行完后再执行g1.switch(),即回到eat函数继续上一次的位置往下执行。这样保证了两个函数都执行完且中间执行过程中做了切换。

    import time
    from  greenlet import greenlet
    def eat():
        print('魔降风云变 is eating')
        g2.switch()
        time.sleep(0.5)
        print('魔降风云变 finished eat')
    
    def sleep():
        print('小马过河 is sleeping')
        time.sleep(0.5)
        print('小马过河 finished sleep')
        g1.switch()
    g1 = greenlet(eat)
    g2 = greenlet(sleep)
    g1.switch()
    ------------结果:
    魔降风云变 is eating
    小马过河 is sleeping
    小马过河 finished sleep
    魔降风云变 finished eat

    3、规避io的原理

    之前的sleep是不能实现规避io的,需要自己写一个去实现规避io。
    [1]

    import time
    def sleep(num):
        t=num+time.time()
        yield t
    sleep(2)
    --------结果:
    没任何反应

    [2]计算N秒之后的时间戳是多少

    import time
    def sleep(num):
        t=num+time.time() 
        yield t
    g=sleep(2) #执行这个函数的意思是计算2秒之后的时间戳是多少
    print(g,type(g))
    for i in g: #t是个生成器,for循环取值
        print(i)
    --------------结果:
    <generator object sleep at 0x005C3300> <class 'generator'>
    1558265393.7168157

    [3]next(生成器对象)取生成器yield返回值

    import time
    def sleep(num):
        t=num+time.time()
        yield t
    g=sleep(2)
    print(next(g))
    ---------结果:
    1558265587.9589255

    [4]获取两秒之后的时间戳

    import time
    def sleep(num):
        t=num+time.time()
        yield t
    g=sleep(2)
    t=next(g)
    print(t)
    -----------结果:
    1558265787.9023616

    [5]获取1秒之后和两秒之后的两个时间戳

    import time
    def sleep(num):
        t=num+time.time()
        yield t
    g1=sleep(1)
    g2=sleep(2)
    t1=next(g1)
    t2=next(g2)
    print(t1)
    print(t2)
    -----------结果:
    1558265947.809565
    1558265948.809565

    [6]获取1秒和2秒后多个时间戳的最小时间戳 #加入列表,列表取最小元素

    import time
    def sleep(num):
        t=num+time.time()
        yield t
    g1=sleep(1)
    g2=sleep(2)
    t1=next(g1)
    t2=next(g2)
    li=[t1,t2]
    min_t=min(li)
    print(min_t)
    ------------结果:
    1558266080.370147

    [7]自己写sleep函数,将sleep(1)sleep(2)总共休眠2次的执行代码改写成用Python原生time函数只是sleep(2),实现类似并发的效果

    import time
    def sleep(num):
        t=num+time.time()
        yield t
        print('sleep结束!')
        yield
    g1=sleep(1)
    g2=sleep(2)
    t1=next(g1)
    t2=next(g2)
    li=[t1,t2]
    min_t=min(li)
    print('通过新的sleep函数执行阻塞多长时间之后的所有时间戳li1:',li)
    print("li1中阻塞时间中离得最近的未来时间:",min_t,'   当前时间:',time.time())
    li.remove(min_t)
    g1zusetime=min_t-time.time()
    print('第一次应阻塞时间长(计算最近的-当前的时间):%s-%s=%s'%(min_t,time.time(),g1zusetime))
    print()
    print('''----------
    将列表中离得最近的未来时间戳删掉,然后用python自带的sleep方法睡未来时间到现在时间的时间差。
    ----------''')
    print()
    time.sleep(g1zusetime)
    min_t=min(li)
    g2zusetime=min_t-time.time()
    print('li2(减去上一次最小值的列表)中阻塞时间列表:',li)
    print("li2中阻塞时间中离得最近的未来时间:",min(li),"执行阻塞第一次应阻塞时间长之后的当前时间:",time.time())
    print('第二次应阻塞时间长(计算最近的-当前的时间):%s-%s=%s'%(min_t,time.time(),g1zusetime))
    
    -------------------------结果:
    
    通过新的sleep函数执行阻塞多长时间之后的所有时间戳li1: [1558268892.4589894, 1558268893.4589894]
    li1中阻塞时间中离得最近的未来时间: 1558268892.4589894    当前时间: 1558268891.4589894
    第一次应阻塞时间长(计算最近的-当前的时间):1558268892.4589894-1558268891.4589894=1.0
    
    ----------
    将列表中离得最近的未来时间戳删掉,然后用python自带的sleep方法睡未来时间到现在时间的时间差。
    
    
    li2(减去上一次最小值的列表)中阻塞时间列表: [1558268893.4589894]
    li2中阻塞时间中离得最近的未来时间: 1558268893.4589894 执行阻塞第一次应阻塞时间长之后的当前时间: 1558268892.4590466
    第二次应阻塞时间长(计算最近的-当前的时间):1558268893.4589894-1558268892.4590466=1.0
    

    [8]自己写的sleep两次阻塞之和3秒时间(实际阻塞2秒,并发,)和Python原生两次阻塞时间之和为3秒(时间阻塞3秒,串行)的实际阻塞时间对比
    #自己写的是将两次阻塞中都要阻塞相同时间的地方只阻塞一次,类似并发。Python原生两次阻塞是阻塞一次,再阻塞一次,时间片没有重叠的部分。
    1)

    import time
    def sleep(num,g):
        t=num+time.time()
        yield t
        print('%ssleep结束!'%g)
        yield
    start=time.time()
    g1=sleep(1,"g1")
    g2=sleep(2,"g2")
    t1=next(g1)
    t2=next(g2)
    li=[t1,t2]
    min_t=min(li)
    
    li.remove(min_t)
    g1zusetime=min_t-time.time()
    time.sleep(g1zusetime)
    g1.__next__()
    
    min_t=min(li)
    g2zusetime=min_t-time.time()
    time.sleep(g2zusetime)
    g2.__next__()
    print("实现并发效果:",time.time()-start)
    -----------结果:
    g1sleep结束!
    g2sleep结束!
    实现并发效果: 2.0001144409179688

    2)

    import time
    start=time.time()
    time.sleep(1)
    time.sleep(2)
    print("python原生sleep方法执行时间:",time.time()-start)
    ----------结果;
    python原生sleep方法执行时间: 3.000171661376953

    #注意:
    1)把sleep当成阻塞,sleep可能是io也可能是在做别的事情,记录什么时候切换回去执行。按照协程的原理去思考。比如网络编程那里recv它能计算到大概什么时候
    2)这个执行next和阻塞时间的计算是第三者帮你做的,靠第三者(不是任务,是调度任务的)来调度的。所有的事情提交给第三者,第三者帮你调度,循环看那些任务遇到阻塞,那些任务阻塞结束,然后进行调度安排切换,执行等等。sleep1 2 对于第三者来说都是一个个事件
    事件循环的概念:第三者一直在循环所有的任务调度所有的任务

    4、gevent模块

    [1]创建协程任务

    #gevent.产卵(任务函数)创建协程任务,就已经开始执行了

    import time
    import gevent
    def eat():
        print('魔降风云变 is eating')
        time.sleep(1)
        print('魔降风云变  finished eat')
    def sleep():
        print('小马过河 is sleeping')
        time.sleep(1)
        print('小马过河 finished sleep')
    g1 = gevent.spawn(eat)  # 创造一个协程任务
    -----------结果:
    没有输出
    #协程遇到阻塞才切换,这里代码从上到下执行结束,没有遇到阻塞

    [2]

    import time
    import gevent
    def eat():
        print('魔降风云变 is eating')
        time.sleep(1)
        print('魔降风云变  finished eat')
    def sleep():
        print('小马过河 is sleeping')
        time.sleep(1)
        print('小马过河 finished sleep')
    g1 = gevent.spawn(eat)  # 创造一个协程任务
    gevent.sleep(2) #加个gevent.sleep(2)就切换到eat执行里面的代码了
    ---------结果:
    魔降风云变 is eating
    魔降风云变  finished eat
    #加个gevent.sleep(2)就切换到eat执行里面的代码了。eat中间time.sleep(1)照样睡。

    [3]

    import time
    import gevent
    def eat():
        print('魔降风云变 is eating')
        time.sleep(1)
        print('魔降风云变  finished eat')
    def sleep():
        print('小马过河 is sleeping')
        time.sleep(1)
        print('小马过河 finished sleep')
    g1 = gevent.spawn(eat)  # 创造一个协程任务
    gevent.sleep(2)
    print('hahahahha')
    ------------结果:
    魔降风云变 is eating
    魔降风云变  finished eat
    hahahahha
    #代码分析:代码从上往下执行,遇到gevent.sleep(2)阻塞了发现还有一个任务,就切到eat函数任务执行,打印'魔降风云变 is eating'之后,这个任务time.sleep(1)1秒,gevent.sleep(2)也睡完一秒了,time.sleep(1)之后打印'魔降风云变  finished eat',任务结束之后切回到这里gevent.sleep(2)又睡了另一秒,然后打印出'hahahahha'。这里总共睡了2秒,而不是3秒。两个任务实现并发
    [4]我将eat里的 time.sleep(1)改为 time.sleep(3)
    #预计打印'魔降风云变 is eating',遇到阻塞应该time.sleep(3)应该回到gevent.sleep(2)先打印'hahahahha',再打印'魔降风云变  finished eat',结果不是的。原因是:time.sleep(3)不是gevent的方法,它不认识的它不切换。那么我是不是要将time.sleep(3)改为gevent.sleep(3)就实现预计的结果呢?看[5]
    import time
    import gevent
    def eat():
        print('魔降风云变 is eating')
        time.sleep(3)
        print('魔降风云变  finished eat')
    def sleep():
        print('小马过河 is sleeping')
        time.sleep(1)
        print('小马过河 finished sleep')
    g1 = gevent.spawn(eat)  # 创造一个协程任务
    gevent.sleep(2)
    print('hahahahha')
    ------------结果:
    魔降风云变 is eating
    魔降风云变  finished eat
    hahahahha

    [5]
    #将time.sleep(3)改为gevent.sleep(3)也没有实现预计的结果。
    原因:
    1)执行代码g1 = gevent.spawn(eat),遇到gevent.sleep(2)阻塞切到g1任务执行eat函数print('魔降风云变 is eating')
    2)遇到gevent.sleep(3)阻塞是会切换到gevent.sleep(2)这边的,因为2秒还没有过所以等待2秒后print('hahahahha')
    3)因为print('hahahahha')代码已经结束没有遇到阻塞,所以没有切回到eat函数执行,函数eat没有执行完程序就结束了。这也不不符合我们的预期效果
    import gevent
    def eat():
    print('魔降风云变 is eating')
    gevent.sleep(3)
    print('魔降风云变 finished eat')
    g1 = gevent.spawn(eat) # 创造一个协程任务
    gevent.sleep(2)
    print('hahahahha')
    -------------结果:
    魔降风云变 is eating
    hahahahha
    [6]由[5]可知,gevent.sleep(2)没有阻塞到切换回去执行eat函数,所以有个协程对象.join(), 阻塞 直到这个协程任务完成为止。
    import time
    import gevent
    def eat():
    print('魔降风云变 is eating')
    gevent.sleep(3)
    print('魔降风云变 finished eat')
    g1 = gevent.spawn(eat) # 创造一个协程任务
    g1.join() # 阻塞 直到g1任务完成为止
    [7]符合预期效果的协程任务演示
    1)创建的g1任务,先执行g1任务,遇到sleep(3)切走到g2任务,g2遇到sleep(1),g2先睡完,然后执行g2任务print('小马过河 finished sleep'),g2任务结束。因为g1.join(),g1任务还没有结束,继续等待。之前执行g2的时候一起睡了1秒了,g2执行完后,g1还需要睡2秒,然后print('魔降风云变 finished eat'),g1任务也结束。
    2)这里因为g2先结束了,在此处g2.join()也可以省略掉。
    3)总结:协程任务创建:gevent.产卵(任务函数)
    协程任务实现调度:协程对象.join()
    import time
    import gevent
    def eat():
    print('魔降风云变 is eating')
    gevent.sleep(3)
    print('魔降风云变 finished eat')
    def sleep():
    print('小马过河 is sleeping')
    time.sleep(1)
    print('小马过河 finished sleep')
    g1 = gevent.spawn(eat) # 创造一个协程任务
    g2 = gevent.spawn(sleep) # 创造一个协程任务
    g1.join() # 阻塞 直到g1任务完成为止
    g2.join()

    [8]协程实现的并发中使用monkey.patch_all()

    #任务函数中time.sleep(3) gevent不认识,改成gevent.sleep(3)。我们用time.sleep(3),但是想要用time.sleep(3),那么就可以从gevnet导入猴子,并执行猴子.patch_all(),这样以后,gevent才能识别出time.sleep(3)。实际上是执行了time.sleep(3)之后,在任务函数中遇到time.sleep(1)就会将time.sleep(1)重写一遍。即执行猴子.patch_all()之后,会在任务函数中将与它本身拥有的方法同名,那就会被重写
    import time
    import gevent
    from gevent import monkey
    monkey.patch_all()
    def eat():
    print('魔降风云变 is eating')
    time.sleep(1)
    print('魔降风云变 finished eat')
    def sleep():
    print('小马过河 is sleeping')
    time.sleep(1)
    print('小马过河 finished sleep')
    g1 = gevent.spawn(eat) # 创造一个协程任务
    g2 = gevent.spawn(sleep) # 创造一个协程任务
    g1.join() # 阻塞 直到g1任务完成为止
    g2.join()
    -----------结果:
    魔降风云变 is eating
    小马过河 is sleeping
    魔降风云变 finished eat
    小马过河 finished sleep
    [9]monkey.patch_all()作用,重写方法。判定是否识别出io操作
    import time
    from gevent import monkey
    print('执行之前',time.sleep)
    monkey.patch_all()
    print('执行之后',time.sleep)
    ------------结果:
    执行之后
    执行之前: <function sleep at 0x022FDDB0>
    [9]gevent.joinall([g1,g2]).g实际.阻塞所有传参协程任务列表
    import time
    import gevent
    from gevent import monkey
    monkey.patch_all()
    def eat():
    print('魔降风云变 is eating')
    time.sleep(1)
    print('魔降风云变 finished eat')
    def sleep():
    print('小马过河 is sleeping')
    time.sleep(1)
    print('小马过河 finished sleep')
    g1 = gevent.spawn(eat) # 创造一个协程任务
    g2 = gevent.spawn(sleep) # 创造一个协程任务
    gevent.joinall([g1,g2])
    -----------结果:
    魔降风云变 is eating
    小马过河 is sleeping
    魔降风云变 finished eat
    小马过河 finished sleep
    #代码分析:
    1)主程序从上到下执行,没有遇到阻塞就没有必要切了。
    2)当我遇到gevent.joinall([g1,g2])阻塞,看两个任务还在不在执行,这时看g1进入阻塞了,又看到g2遇到阻塞了,它又回到gevent.joinall([g1,g2])这个阻塞,于是程序就来回在g1和g2之间互相切换,这两个任务谁先结束阻塞就先执行哪个任务。等到这两个任务结束了,gevent.joinall([g1,g2])阻塞也结束了。
    [10]批量创建多个协程
    import time
    import gevent
    from gevent import monkey
    monkey.patch_all()
    def eat():
    print('魔降风云变 is eating')
    time.sleep(1)
    print('魔降风云变 finished eat')

    g_li=[]
    for i in range(10):
    g=gevent.spawn(eat())
    g_li.append(g)
    for g in g_li:g.join()
    [10]协程获取返回值:协程对象.value属性
    1)没有执行完协程任务就拿返回值#拿到的是默认返回值
    import time
    import gevent
    from gevent import monkey
    monkey.patch_all()
    def eat():
    print('魔降风云变 is eating')
    time.sleep(1)
    print('魔降风云变 finished eat')
    return "魔降风云变"
    def sleep():
    print('小马过河 is sleeping')
    time.sleep(1)
    print('小马过河 finished sleep')
    return "小马过河"
    g1=gevent.spawn(eat)
    g2=gevent.spawn(sleep)
    print(g1.value,g2.value)
    --------结果:
    None None
    #原因:函数没有执行,等协程任务执行完需要加阻塞

    2)执行完协程任务就拿返回值

    import time
    import gevent
    from gevent import monkey
    monkey.patch_all()
    def eat():
    print('魔降风云变 is eating')
    time.sleep(1)
    print('魔降风云变 finished eat')
    return "魔降风云变"
    def sleep():
    print('小马过河 is sleeping')
    time.sleep(1)
    print('小马过河 finished sleep')
    return "小马过河"
    g1=gevent.spawn(eat)
    g2=gevent.spawn(sleep)
    gevent.joinall([g1,g2])
    print(g1.value,g2.value)
    -----------结果:
    魔降风云变 is eating
    小马过河 is sleeping
    魔降风云变 finished eat
    小马过河 finished sleep
    魔降风云变 小马过河

    5、asyncio学习

    5.1 起一个协程任务

    import asyncio
    async def demo(): # 协程方法
    print('start')
    await asyncio.sleep(1) # 阻塞
    print('end')

    loop = asyncio.get_event_loop() # 创建一个事件循环
    loop.run_until_complete(demo()) # 把demo任务丢到事件循环中去执行
    -----------结果:
    start
    end

    #定义一个任务:
    async 在定义函数前,函数中的阻塞前面加await,阻塞事件用asyncio的方法,比如asyncio.sleep(1)
    #asyncio在做协程的切换时只认自己实现的方法 #await后面跟的是协程函数和方法,是一个阻塞事件。await这个关键字的使用需要在使用它的函数前面添加async关键字标识它是一个协程函数
    ##这个协程函数不能自己去调用,而是asyncio的方法去调用
    #调用方法:
    创建对象:模块.获取事件循环()
    对象.运行直到完成(协程执行函数())#注意,这里需要执行
    注意:不能对象.运行直到完成做多个任务,这样就不能实现协程的并发执行

    5.2启动多个任务,并且没有返回值

    import asyncio
    async def demo(): # 协程方法
    print('start')
    await asyncio.sleep(1) # 阻塞
    print('end')
    loop = asyncio.get_event_loop() # 创建一个事件循环
    wait_obj = asyncio.wait([demo(),demo(),demo()])
    loop.run_until_complete(wait_obj)
    -------------结果;
    start
    start
    start
    end
    end
    end

    启动多个任务:
    创建协程事件循环对象对象:模块.获取事件循环()
    创建任务对象:模块.等待(协程函数列表)
    运行:事件循环对象.运行直到完成(任务对象)

    5.3启动多个任务并且有返回值

    import asyncio
    async def demo(): # 协程方法
    print('start')
    await asyncio.sleep(1) # 阻塞
    print('end')
    return 123
    loop = asyncio.get_event_loop()
    t1 = loop.create_task(demo())
    t2 = loop.create_task(demo())
    tasks = [t1,t2]
    wait_obj = asyncio.wait([t1,t2])
    loop.run_until_complete(wait_obj)
    for t in tasks:
    print(t.result())
    ----------------结果:
    start
    start
    end
    end
    123
    123

    启动多个任务并且有返回值:
    创建协程事件循环对象对象:模块.获取事件循环()
    创建多个协程任务:事件循环对象.创建任务(函数),变量接收以供使用
    创建任务对象:模块.等待(协程任务列表)
    运行:事件循环对象.运行直到完成(任务对象)
    打印每个协程任务的返回值:for循环每个协程任务,然后打印任务.结果(),这里打印有阻塞,是同步的,想异步即谁先执行完先打印谁用await打印

    5.4启动多个任务并且有返回值,打印返回值谁先执行完先打印谁。(跟线程里的回调函数很像)

    import asyncio
    async def demo(): # 协程方法
    print('start')
    await asyncio.sleep(1) # 阻塞
    print('end')
    return 123
    async def main():
    task_l = []
    for i in range(3):
    task = asyncio.ensure_future(demo())
    task_l.append(task)
    for ret in asyncio.as_completed(task_l):
    res = await ret
    print(res)

    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    --------------结果:
    start
    start
    start
    end
    end
    end
    123
    123
    123

    启动多个任务并且有返回值,哪个返回值先到先处理哪个返回值,
    创建协程事件循环对象对象:模块.获取事件循环()
    再创建一个对返回值做处理的协程函数,函数里for循环创建任务:模块.确认未来(要运行的协程函数),每个任务追加到列表,对列表进行模块.完成的(这个列表)循环取值,每个值用await解决阻塞。返回值谁先加到列表就先打印谁。
    如果不是这样,那么for循环列表返回值,因为列表前面的任务没有执行完,没有返回值,那么就会等待前面的那个执行完打印了它的返回值才能继续往下打印,这样后面的协程先执行完的就只能因为前面的协程没有执行完而处于等待的状态。

    事件循环对象.运行直到完成(处理返回值的函数)

    实现原理简化:
    asyncio.ensure_future(demo())执行

    import asyncio
    async def demo(): # 协程方法
    await asyncio.sleep(1) # 阻塞
    return 123
    async def main():
    task_l = []
    for i in range(3):
    task = asyncio.ensure_future(demo())
    print('task',task)
    task_l.append(task)
    bb=asyncio.as_completed(task_l)
    print('bb:',bb)
    for ret in bb:
    print('ret',ret)
    res = await ret
    print('res',res)

    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    -----------结果:
    task <Task pending coro=<demo() running at C:/mcw/study/test.py:2>>
    task <Task pending coro=<demo() running at C:/mcw/study/test.py:2>>
    task <Task pending coro=<demo() running at C:/mcw/study/test.py:2>>
    bb: <generator object as_completed at 0x02AC1660>
    ret <generator object as_completed.._wait_for_one at 0x02AC1720>
    res 123
    ret <generator object as_completed.._wait_for_one at 0x02AC1750>
    res 123
    ret <generator object as_completed.._wait_for_one at 0x02AC1720>
    res 123

    5.4验证5.3返回值谁先执行完先打印谁

    import asyncio
    async def demo(i): # 协程方法
    print('start')
    await asyncio.sleep(10-i) # 阻塞
    print('end')
    return i,123

    async def main():
    task_l = []
    for i in range(4):
    task = asyncio.ensure_future(demo(i))
    task_l.append(task)
    for ret in asyncio.as_completed(task_l):
    res = await ret
    print(res)

    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    ------------结果:
    start
    start
    start
    start
    end
    (3, 123)
    end
    (2, 123)
    end
    (1, 123)
    end
    (0, 123)

    验证原理:
    task_l列表里面for循环创建并执行任务,然后追加到列表里的都是有序的任务。将第i次创建的任务传进任务函数作为任务函数的返回值,正常for循环打印task_l里的结果是有序的,现在无序打印,所有回值谁先执行完先打印谁的

    #asyncio小结:

    语法

    # await 阻塞 协程函数这里要切换出去,还能保证一会儿再切回来
    # await 必须写在async函数里,async函数是协程函数
    # loop 事件循环
    # 所有的协程的执行 调度 都离不开这个loop

    6、asyncio实现爬虫

    import asyncio
    async def get_url():
    reader,writer = await asyncio.open_connection('www.baidu.com',80)
    writer.write(b'GET / HTTP/1.1 HOST:www.baidu.com Connection:close ')
    all_lines = []
    async for line in reader:
    data = line.decode()
    all_lines.append(data)
    html = ' '.join(all_lines)
    return html

    async def main():
    tasks = []
    for url in range(20):
    tasks.append(asyncio.ensure_future(get_url()))
    for res in asyncio.as_completed(tasks):
    result = await res
    print(result)

    if name == 'main':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main()) # 处理一个任务
    -----------------结果:
    HTTP/1.1 200 OK
    Accept-Ranges: bytes
    ...........

    python原生的底层的协程模块

    # 爬虫 webserver框架
    # 题高网络编程的效率和并发效果
  • 相关阅读:
    raw socket
    selenium and win32api
    linux ip
    network config
    grub paramiter & menu.list
    实用命令dd
    resin or tomcat .war e.g. note
    JSP 运行
    linux command screen
    docker interact example
  • 原文地址:https://www.cnblogs.com/machangwei-8/p/10907572.html
Copyright © 2011-2022 走看看