zoukankan      html  css  js  c++  java
  • 单线程下实现IO切换

    1、Greenlet

    greenlet可以实现两个任务之间的来回切换,但遇到IO会阻塞,不会切(使用这个模块之前需要在电脑命令提示符中输入 pip3 install greenlet 进行安装)

    例如:

    from greenlet import greenlet
    import time
    
    def eat(name):
        print('%s eat 1' %name)
        time.sleep(30)            #遇到IO不会切
        g2.switch('alex')
        print('%s eat 2' %name)
        g2.switch()
    def play(name):
        print('%s play 1' %name)
        g1.switch()
        print('%s play 2' %name)
    
    g1=greenlet(eat)
    g2=greenlet(play)
    
    g1.switch('egon')

    打印结果:

    egon eat 1
    alex play 1
    egon eat 2
    alex play 2

     2、gevent

    先在命令提示符中输入 pip3 install gevent 进行安装

    Gevent 是一个第三方库,可以轻松通过gevent实现并发同步或异步编程,它遇到IO阻塞时会自动切换任务。

     #用法

    g1=gevent.spawn(func,1,,2,3,x=4,y=5)创建一个协程对象g1,spawn括号内第一个参数是函数名,如eat,后面可以有多个参数,
    可以是位置实参或关键字实参,都是传给函数eat的
    g2
    =gevent.spawn(func2) g1.join() #等待g1结束 g2.join() #等待g2结束 #或者上述两步合作一步:gevent.joinall([g1,g2]) g1.value#拿到func1的返回值
    import gevent
    
    def eat(name):
        print('%s eat 1' %name)
        gevent.sleep(5)
        print('%s eat 2' %name)
    def play(name):
        print('%s play 1' %name)
        gevent.sleep(3)
        print('%s play 2' %name)
    
    g1=gevent.spawn(eat,'egon')
    g2=gevent.spawn(play,'alex')
    
    # gevent.sleep(100)
    # g1.join()
    # g2.join()
    gevent.joinall([g1,g2])

    打印结果:

    egon eat 1
    alex play 1
    alex play 2
    egon eat 2

    上例gevent.sleep(2)模拟的是gevent可以识别的io阻塞,

    而time.sleep(2)或其他的阻塞,gevent是不能直接识别的需要用下面一行代码,打补丁,就可以识别了

    from gevent import monkey;monkey.patch_all()必须放到被打补丁者的前面,如time,socket模块之前

    或者我们干脆记忆成:要用gevent,需要将from gevent import monkey;monkey.patch_all()放到文件的开头

    monkey.patch_all(),相当于把这个程序里只要涉及到IO操作的都打了一个标记,这样可以识别time模块下的IO行为。

    from gevent import monkey;monkey.patch_all()
    import gevent
    import time
    
    def eat(name):
        print('%s eat 1' %name)
        time.sleep(5)
        print('%s eat 2' %name)
    def play(name):
        print('%s play 1' %name)
        time.sleep(3)
        print('%s play 2' %name)
    
    g1=gevent.spawn(eat,'egon')
    g2=gevent.spawn(play,'alex')
    
    # gevent.sleep(100)
    # g1.join()   保证主线程不死掉
    # g2.join() 
    gevent.joinall([g1,g2])

    运行结果和上面程序的结果相同

    使用current_thread 可以看线程名

    from gevent import monkey;monkey.patch_all()
    from threading import current_thread
    import gevent
    import time
    
    def eat():
        print('%s eat 1' %current_thread().name)
        time.sleep(5)
        print('%s eat 2' %current_thread().name)
    def play():
        print('%s play 1' %current_thread().name)
        time.sleep(3)
        print('%s play 2' %current_thread().name)
    
    g1=gevent.spawn(eat) 
    g2=gevent.spawn(play)
    
    # gevent.sleep(100)
    # g1.join()
    # g2.join()
    print(current_thread().name)
    gevent.joinall([g1,g2])

    打印结果:

    MainThread
    DummyThread-1 eat 1
    DummyThread-2 play 1
    DummyThread-2 play 2
    DummyThread-1 eat 2

    注:DummyThread-1,和DummyThread-2是两个假线程,干活的其实都还是主线程在干,没有开过新的线程

    spawn提交任务的方式是异步提交,提交完之后不会等在原地,再接着提交下一个任务

     学完gevent模块后,可以这样实现并发:

    1、开多个进程,用上了多核优势

    2、每个进程里再开线程,提高了并发数

    3、每个线程使用gevent模块方式实现遇到IO切,把单个线程的效率提到最高

    这样做极大的提升了程序的效率

  • 相关阅读:
    从无到有构建vue实战项目(二)
    从无到有构建vue实战项目(一)
    windows下nginx的安装和使用
    系统全局环境变量设置
    100个常用的linux命令
    Linux常用系统信息查看命令
    linux下搜索find命令拾遗
    linux基础优化
    linux系统基础文件属性
    正则awk和查看文件行数
  • 原文地址:https://www.cnblogs.com/zh-xiaoyuan/p/11779595.html
Copyright © 2011-2022 走看看