zoukankan      html  css  js  c++  java
  • python---基础知识回顾(十)进程和线程(协程gevent:线程在I/O请求上的优化)

    优点:使用gevent协程,可以更好的利用线程资源。(基于线程实现)

    需求:使用一个线程,去请求多个网站的资源(注意,请求上会有延时)<实际上是去请求了大量的网站信息,我们使用了多线程,只不过每个线程依旧会分配到多个网站资源,这里我们只需要去讨论这一条线程即可>

    可以看出,由于网络延迟等因素,当我们去获取信息时,有一段时间呗浪费,用于空等信息返回,当我们去获取大量网站的时候,那这个时间是非常大的。我们需要去避免他。

    解决方案:使用协程,充分利用我们中间等待的这一段时间,去做其他的事情,比如其请求下一个网站,,或者下几个网站。然后连续去接收信息,就可以充分的利用空耗的时间

    1.协程的简单使用:

    pip3 install gevent  # gevent模块若是没有,只需要先下载

    开始使用:

    import gevent
    from gevent import monkey
    monkry.patch_all()  #可以提高效率
    def foo(): print(
    "foo函数开始运行") gevent.sleep(0) print("又回到了foo函数") def bar(): print("bar函数开始运行") gevent.sleep(0) print("又回到了bar函数") gevent.joinall([ gevent.spawn(foo), gevent.spawn(bar), ])
    输出结果:
    foo函数开始运行 bar函数开始运行 又回到了foo函数 又回到了bar函数

    2.协程的了解:对于上面的例子来说,有点不太容易理解,我们使用计时去了解其中流程,再去讨论上面代码

    (1)上面sleep(0)和下面的sleep(3)相比,得出两个函数的执行时间是一致的(几乎是)

    import gevent
    import time
    
    begin = time.time()
    
    def foo():
        fs = time.time() - begin
        print("foo函数开始运行",fs)
    gevent.sleep(
    3) fe = time.time() - begin print("又回到了foo函数",fe) def bar(): bs = time.time() - begin print("bar函数开始运行",bs)
    gevent.sleep(
    3)
    be
    = time.time() - begin print("又回到了bar函数",be) gevent.joinall([ gevent.spawn(foo), gevent.spawn(bar), ])
    foo函数开始运行 0.01000070571899414
    bar函数开始运行 0.01000070571899414
    又回到了foo函数 3.0101723670959473
    又回到了bar函数 3.0101723670959473

    注意输出结果

    我们可以看出两个函数都是在统一时间执行第一句输出,在三秒后去执行的第二句输出

     (2)sleep(3)和sleep(1)

    import gevent
    import time
    
    begin = time.time()
    
    def foo():
        fs = time.time() - begin
        print("foo函数开始运行",fs)
    gevent.sleep(
    1) fe = time.time() - begin print("又回到了foo函数",fe) def bar(): bs = time.time() - begin print("bar函数开始运行",bs)
    gevent.sleep(
    3)
    be
    = time.time() - begin print("又回到了bar函数",be) gevent.joinall([ gevent.spawn(foo), gevent.spawn(bar), ])

    注意输出结果:几乎在同一时间执行两个函数(顺序和joinall方法中注册顺序有关),在我们设定的sleep时间后去继续执行函数

    foo函数开始运行 0.0060002803802490234
    bar函数开始运行 0.0060002803802490234
    又回到了foo函数 1.0060575008392334
    又回到了bar函数 3.006171941757202

    所以说对于最上面简单使用中的执行顺序先是根据joinall的注册顺序去打印

    foo函数开始运行
    bar函数开始运行

    然后由于sleep(0)间隔是0,所以立即去执行下面的打印程序(当sleep的时间是一致时,顺序还是和注册时一致)

    又回到了foo函数
    又回到了bar函数

    (3)使用time.sleep()去更加深刻了解协程

    import gevent
    import time
    
    begin = time.time()
    
    def foo():
        fs = time.time() - begin
        print("foo函数开始运行",fs)
    gevent.sleep(
    1)
    time.sleep(
    4)  #这里睡眠4秒 fe = time.time() - begin print("又回到了foo函数",fe) def bar(): bs = time.time() - begin print("bar函数开始运行",bs)
    gevent.sleep(
    3)
    be
    = time.time() - begin print("又回到了bar函数",be) gevent.joinall([ gevent.spawn(foo), gevent.spawn(bar), ])

    注意输出结果:发现对于我们在foo中设置的time.sleep(4)对bar方法也有影响。

    foo函数开始运行 0.005000114440917969
    bar函数开始运行 0.0060002803802490234
    又回到了foo函数 5.006286144256592
    又回到了bar函数 5.007286310195923

    原因:gevent设置了我们协程的苏醒时间,但是当苏醒时间与我们的执行时间相冲突,那么会以执行时间为主(毕竟这是单线程,不会考虑其他的),而原来的设置的gevent.sleep(秒数)则变成了大小比较,谁在后,谁就后执行

     任务框架:

    import gevent
    import time
    
    begin = time.time()
    
    def foo(url,index):
        fs = time.time() - begin
        print("%s:发送请求到%s,等待返回"%(index,url),fs) #这里可以模拟发送请求
        gevent.sleep(0)
        fe = time.time() - begin
        print("%s:获取信息从%s,开始处理"%(index,url),fe) #这里模拟处理信息
    
    gevent.joinall([
        gevent.spawn(foo,"www.baidu.com",1),  #注意传参方式
        gevent.spawn(foo,"www.sina.com.sn",2),
    ])

    输出结果:

    1:发送请求到www.baidu.com,等待返回 0.005000114440917969
    2:发送请求到www.sina.com.sn,等待返回 0.005000114440917969
    1:获取信息从www.baidu.com,开始处理 0.005000114440917969
    2:获取信息从www.sina.com.sn,开始处理 0.005000114440917969


     补充:greenlet协程(gevent是基于greenlet实现,所以有必要去了解下)

    from greenlet import greenlet
    
    def foo():
        print("开始执行foo")
        gr2.switch()
        print("又回到foo")
        gr2.switch()
    
    def bar():
        print("开始执行bar")
        gr1.switch()
        print("又回到bar")
    
    gr1 = greenlet(foo)
    gr2 = greenlet(bar)
    gr1.switch()    #以gr1开始执行,switch中也可以传递参数

    输出结果:

    开始执行foo
    开始执行bar
    又回到foo
    又回到bar
  • 相关阅读:
    override new virtual 的比较
    c#页面无内容解决方案
    插入排序
    排序算法(转)
    treenode遍历文件夹
    案例篇(1)
    索引器(转)
    迭代器的实现
    抽象类和接口的区别
    索引器与迭代器,属性的区别
  • 原文地址:https://www.cnblogs.com/ssyfj/p/9030165.html
Copyright © 2011-2022 走看看