• 并发编程之协程


    一、协程概述

    1、什么是协程?

    协程也被称为“微线程”,在一个线程中规定某个代码块的执行顺序线程和进程的操作是由程序触发系统接口,最后的执行者是系统;协程的操作则是程序员。

    2、为什么会有协程?

      对于多线程应用,CPU通过切片的方式来切换线程间的执行,线程切换时需要耗时(保存状态,下次继续)。协程,则只使用一个线程,在一个线程中规定某个代码块执行顺序。这样更加节约切换的时间。

    3、协程的特点?

    • 不需要多线程的锁机制

    因为只有一个线程,不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。

    • 可以利用多核特点

    通过多进程 + 协程既可以利用多核又可以发挥协程的高效率。

    • 从技术的角度来说,“协程就是你可以暂停执行的函数”

    类似于python中的生成器

    4、协程的应用场景

    当程序中存在大量不需要CPU的操作时(IO),适用于协程。

    二、greenlet、gevent模块

    (一)greenlet

    上面说了从技术层面上说“协程就是你可以暂停执行的函数”,相当于一个生成器,greenlet是一个用C实现的协程模块:

    from greenlet import greenlet
    
    def test1():
        print(9)    
        gr2.switch() #手动切换执行test2
        print(22)
        gr2.switch() #手动切换执行test2
    
    
    def test2():
        print(2)
        gr1.switch() #手动切换执行test1
        print(36)
    
    
    gr1 = greenlet(test1)
    gr2 = greenlet(test2)
    gr1.switch()  #开始执行test1
    """
    输出:
    9
    2
    22
    36
    """

    可以看出来greenlet需要手动控制来执行代码块的。

    (二)gevent

    这个模块比上一个模块智能许多,它不需要手动控制代码块的执行顺序,只是如果协程碰到I/O操作,就会自动切换。

    import gevent
    
    def test1():
        print(9)  #第2步
        gevent.sleep(1) #第3步
        print(22) #第7步
    
    def test2():
        print(2)   #第4步
        gevent.sleep(0.3) #第5步
        print(36)  #第6步
    
    gevent.joinall([
        gevent.spawn(test1), #第1步
        gevent.spawn(test2),
    ])
    """
    输出:
    9
    2
    36
    22
    """

     当然,还可以使用gevent模块进行爬虫:

    import gevent
    import requests
    
    from gevent import monkey
    monkey.patch_all()
    
    def task(method,url,req_kwargs):
        response=requests.request(method=method,url=url,**req_kwargs)
        print(response.url,response.content)
    
    #发送请求
    gevent.joinall(
        {
            gevent.spawn(task,method='get',url='http://baidu.com/',req_kwargs={}),
            gevent.spawn(task, method='get', url='https://www.tudou.com/', req_kwargs={})
    
        }
    )
    gevent爬网页内容
    import gevent
    import requests
    from gevent.pool import Pool
    
    from gevent import monkey
    monkey.patch_all()
    
    def task(method,url,req_kwargs):
        response=requests.request(method=method,url=url,**req_kwargs)
        print(response.url,response.content)
    
    #发送请求
    pool=Pool(5)
    gevent.joinall(
        {
            pool.spawn(task,method='get',url='http://baidu.com/',req_kwargs={}),
            pool.spawn(task, method='get', url='https://www.tudou.com/', req_kwargs={})
    
        }
    )
    协程池发送请求

    上面使用的是利用gevent进行并发发送请求,requests爬取网页内容,而grequests模块将两者进行结合,既有并发的特性又有requests的特性。

    #利用grequests进行并发 将requests和gevent进行封装
    
    import grequests
    
    request_list=[
        grequests.get('http://baidu.com/'),
        grequests.get('https://www.tudou.com/')
    ]
    response_list=grequests.map(request_list,size=5)
    print(response_list) #[<Response [200]>, <Response [200]>]
    grequests模块
  • 相关阅读:
    转发URL请求
    服务端使用Zookeeper注册服务地址,客户端从Zookeeper获取可用的服务地址。
    Boss Group Worker Group NioEventLoopGroup
    Java NIO vs. IO
    解决了网关所面临的依赖于后端接口服务的上线问题
    Dealing with a Stream-based Transport 处理一个基于流的传输 粘包 即使关闭nagle算法,也不能解决粘包问题
    use Properties objects to maintain its configuration Writing Reading System Properties 维护配置 系统变量
    即使关闭了nagle算法,粘包依旧存在
    解Bug之路-TCP粘包Bug
    Netty 粘包/半包原理与拆包实战
  • 原文地址:https://www.cnblogs.com/shenjianping/p/11604817.html
Copyright © 2020-2023  润新知