• python之路——协程(greenlet、gevent、简单爬虫)


      协程  

    协程,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程

    协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此:

    协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态,换种说法:进入上一次离开时所处逻辑流的位置。

    协程的好处:

    • 无需线程上下文切换的开销
    • 无需原子操作锁定及同步的开销
    • "原子操作(atomic operation)是不需要synchronized",所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体是原子性的核心。
    • 方便切换控制流,简化编程模型
    • 高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

     缺点:

    • 无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。
    • 进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

    使用yield实现协程操作例子

    import time
    
    def consumer(name):
        print("开始吃包子")
        while True:
            bun = yield            #使函数挂起
            print(name,"正在吃",bun)
    
    def producer():
        r = con.__next__()
        r = con2.__next__()
        n = 0
        while n < 5:
            n += 1
            con.send(n)
            con2.send(n)
            time.sleep(2)
            print("正在制作包子",n)
    
    if __name__ == '__main__':
        con = consumer("alex")
        con2 = consumer("japhi")
        p = producer()
    

    协程必须满足以下条件:

    1. 必须在只有一个单线程里实现并发
    2. 修改共享数据不需加锁
    3. 用户程序里自己保存多个控制流的上下文栈
    4. 一个协程遇到IO操作自动切换到其它协程

      手动切换(Greenlet)  

    greenlet是一个用C实现的协程模块,相比与python自带的yield,它可以使你在任意函数之间随意切换,而不需把这个函数先声明为generator

    import greenlet
    def main():
        print("1111111")
        gr2.switch()
        print("3333333")
        gr2.switch()
    
    def next():
        print("2222222")
        gr1.switch()
        print("4444444")
    
    if __name__ == "__main__":
        gr1 = greenlet.greenlet(main)    #实例化一个协程对象,括号内是函数
        gr2 = greenlet.greenlet(next)
        gr1.switch()   #手动切换
    
    输出结果:
    1111111
    2222222
    3333333
    4444444
    

    先实例化对象,然后调用手动切换方法switch 

      自动切换(Gevent) 

    Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

    import gevent
    
    def first():
        print(1111)
        gevent.sleep(3)   #模拟io切换,自动切换
        print(22222)
    
    def second():
        print(33333)
        gevent.sleep(2)   #模拟io切换,自动切换
        print(44444)
    def third():
        print(55555)
        gevent.sleep(1)   #模拟io切换,自动切换
        print(666666)
    
    gevent.joinall([      #需要把函数全都加入进去
        gevent.spawn(first),
        gevent.spawn(second),
        gevent.spawn(third)
    ])
    
    
    输出:
    1111
    33333
    55555
    666666
    44444
    22222
    

    利用gevent实现简单爬虫

    from urllib import request
    import gevent,time
    from gevent import monkey
    monkey.patch_all()         #添加io操作标准,让程序可以并行下载网页
    
    def net(url):
        resq = request.urlopen(url)
        data = resq.read()
        print(len(data))
    
    urls = ['https://www.baidu.com/',
            'https://www.hao123.com/',
            'https://www.sina.com/' ]
    time_start = time.time()
    for url in urls:
        net(url)
    print("同步cost",time.time() - time_start)
    
    async_time_start = time.time()
    gevent.joinall([
        gevent.spawn(net,"https://www.baidu.com"),
        gevent.spawn(net,"https://www.hao123.com"),
        gevent.spawn(net,"https://www.sina.com/")
    ])
    print("异步cost",time.time() - async_time_start)
    
    输出结果:
    227
    514581
    601335
    同步cost 2.6131491661071777
    227
    514599
    601338
    异步cost 2.2071263790130615
    

    异步并行所花的时间少于同步,但要加  monkey.patch_all()           #添加io操作标准,让程序可以并行下载网页

     

  • 相关阅读:
    LeetCode 242. Valid Anagram (验证变位词)
    LeetCode 205. Isomorphic Strings (同构字符串)
    LeetCode 204. Count Primes (质数的个数)
    LeetCode 202. Happy Number (快乐数字)
    LeetCode 170. Two Sum III
    LeetCode 136. Single Number (落单的数)
    LeetCode 697. Degree of an Array (数组的度)
    LeetCode 695. Max Area of Island (岛的最大区域)
    Spark中的键值对操作
    各种排序算法总结
  • 原文地址:https://www.cnblogs.com/japhi/p/7094515.html
Copyright © 2020-2023  润新知