zoukankan      html  css  js  c++  java
  • 协程----greenlet模块,gevent模块

    1.协程初识,greenlet模块

    2.gevent模块(需要pip安装)

    一.协程初识,greenlet模块:


    协程:是单线程下的并发,又称微线程,纤程。英文名Coroutine。一句话说明什么是线程:协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的。

    greenlet模块要自己用pip安装

    #协程:
        #本质上是一个线程
        #能够在多个任务之间切换来节省一些IO时间
        #协程中任务之间的切换也消耗时间,但是开销远远小于进程线程之间的切换
    
    #协程的意义:
        #在遇到IO操作的时候,切换到另外一个任务
        #规避之前任务的IO时间,来提高cpu的利用率
        #在实际工作中会采用:进程+线程+协程,来提高代码的并发效果
        #进程是cpu核数+1,线程是cpu核数*5,每个线程中协程最多可以起500个
    #比如:
        #发送了一个网页请求后,在网络延时,等待网页响应的时间(等待IO),就可以用协程去切换任务利用等待的时间,继续发送多个网页请求,从而提高效率
        #进程5,线程20,协程500 = 总共可以有50000个协程:一台4c的机器最多可以接收的并发数
    #数据库,负载均衡,让很多个请求,平均分摊给各个服务器
        #nginx组件 大型互联网公司会用到,就是用来帮你分发任务的,并发最大承载量就是50000,用的就是协程机制
        #一般情况下就是根据这个规则,上下浮动
    
    #协程的调度是由gevent完成的,而进程和线程的调度是cpu完成的
    
    #greenlet以后都不怎么样,协程还是主要用gevent
    #真正的协程模块就是使用greenlet完成的切换
    from greenlet import greenlet
    def eat():
        print('eating')
        g2.switch()       #切换到g2运行g2,切记录当前g1运行的位置,切换后,如果g2那没切换回来,后面这句    print('eat end') 就不会运行了
        print('eat end')
    
    def play():
        print('playing')
        g1.switch()
        print('play end')
    
    g1 = greenlet(eat)
    g2 = greenlet(play)
    g1.switch()
    
    # def play2():
    #     print('playing')
    #     g1.switch()
    #     print('play end')
    #
    # def ch():
    #     play2.__name__ = 'play3'
    # ch()
    # print(play2.__name__)
    协程初识-greenlet模块

    #协程
    #由于cpython解释器中的GIL原因,导致python中多线程被弱化了,而且切换多个线程之间也要时间开销
    #所以就出现了协程,协程的切换效率更快,把1个线程的作用发挥到了极致,提高1个cpu的利用率。减少线程的时间开销
    #java里也有协程,但是没有这么被重视

    二.gevent模块


    安装:pip3 install gevent

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

    #切记 from gevent import monkey;monkey.patch_all()  这句话一定要写在想更改的模块前面

    #GKX
    #gevent模块  join,joinall,value,spawn
    #协程适用于网络延迟的时候,也就是适合做爬虫的时候 ,或者socket连接的时候。代码如果没有高IO,没必要用协程
    #gevent只会识别它认识的IO操作
    from gevent import monkey;monkey.patch_all() #monkey这句话一定要写在想更改的模块前面
    import time  #通过猴子补丁,让sleep不阻塞
    import gevent
    import threading
    # 用过gevent就会知道,会在最开头的地方gevent.monkey.patch_all();把标准库中的thread/socket等给替换掉.
    # 这样我们在后面使用socket的时候可以跟平常一样使用,无需修改任何代码,但是它变成非阻塞的了.
    
    from greenlet import greenlet
    def eat():
        print(threading.current_thread().getName())  #>>DummyThread-1  dummy(仿制品)
        print('eating')
        time.sleep(1)    #使用猴子补丁后,相当于 gevent.sleep
        # gevent.sleep(1)
        print('eat end')
    
    def play():
        print(threading.current_thread().getName())
        print('playing')
        # time.sleep(1)
        gevent.sleep(1)
        print('play end')
    g1 = gevent.spawn(eat)  #开启一个协程, spawn(大量生产意思)
    g2 = gevent.spawn(play)
    g1.join() #让线程等待协程的结果,如果没有join,线程执行完毕后直接关闭,等不到协程的结果
    g2.join()
    print('11111111111111')
    #遇到sleep后会非阻塞,先执行其他任务,然后再同时来sleep1秒来打印任务
    #有了gevent和猴子补丁,只要把函数注册进 gevent模块里,就可以使用协程了,其他都不用管
    #当遇到IO会自动帮你切换到其他任务,最后再一起共享,所有任务的IO操作
    
    #遇到IO—执行其他任务—其他任务执行到也遇到了IO——继续执行其他任务—都遇到了IO—在IO之间来回切换,看谁解除了IO,马上继续运行
    #从而把等待IO的时间,用来执行任务。然后共享IO时间,达到提高效率的目的
    #我们可以不用理会gevent的执行过程,注册完线程运行等待结果就好了
    gevent模块-例子和解释
    from gevent import monkey;monkey.patch_all() #monkey这句话一定要写在想更改的模块前面
    import time  #通过猴子补丁,让sleep不阻塞
    import gevent
    import threading
    #同步和异步
    def task():
        time.sleep(0.5)
        print('12345')
    
    def sync():
        for i in range(10):
            task()
    
    def a_sync():
        g_lst = []
        for i in range(10):
            g = gevent.spawn(task)
            g_lst.append(g)
        gevent.joinall(g_lst) # == for g in g_lst:g.join()
    
    sync()
    a_sync()
    同步和异步的比较
    # monkey patch指的是在运行时动态替换,一般是在startup的时候.
    # 用过gevent就会知道,会在最开头的地方gevent.monkey.patch_all();把标准库中的thread/socket等给替换掉.
    # 这样我们在后面使用socket的时候可以跟平常一样使用,无需修改任何代码,但是它变成非阻塞的了.
    # 之前做的一个游戏服务器,很多地方用的import json,后来发现ujson比自带json快了N倍,
    # 于是问题来了,难道几十个文件要一个个把import json改成import ujson as json吗?
    # 其实只需要在进程startup的地方monkey patch就行了.是影响整个进程空间的.同一进程空间中一个module只会被运行一次.
    # 下面是代码:
    
    # import json
    # import ujson
    #
    # def monkey_patch_json():
    #     json.__name__ = 'ujson'
    #     json.dumps = ujson.dumps
    #     json.loads = ujson.loads
    #
    # monkey_patch_json()
    #
    # print
    # 'main.py', json.__name__
    # import sub
    #
    #
    # import json
    # print 'sub.py',json.__name__
    
    # 最后,注意不能单纯的json = ujson来替换.
    猴子补丁

    用gevent模块实现的socket服务端的并发:

    from gevent import monkey;monkey.patch_all()

    from gevent import monkey;monkey.patch_all()
    import gevent
    
    
    def func(conn):
        conn.send(b'hello')
        msg = conn.recv(1024).decode('utf8')
        print(msg)
        conn.close()  #关闭如果放在循环里,就马上关闭了,没有连接的意义
    
    while True:
        conn, addr = sk.accept()  #在这里阻塞,一直监听,一旦有client连接,马上把conn扔给一个并发。然后继续循环,阻塞
        g = gevent.spawn(func,conn)
    
    sk.close()
    server端
    import time
    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',8080))
    
    print(sk.recv(1024))
    msg = input('>>>> ').encode('utf8')
    sk.send(msg)
    sk.close()
    client端
  • 相关阅读:
    一顿午饭引发的风波
    用exp无法导出空表解决方法/用exp导出数据时表丢失原因
    程序员的不归路
    IIS权限设置
    超级基础搭建Android开发环境
    业务流程图与数据流程图的比较
    发布asp.net应用程序后,其中导入、导出excel报错的解决方案
    Validation of viewstate MAC failed异常的原因及解决方法
    orarcle11g中服务代表的意思
    最新BIOS设置中英文对照表
  • 原文地址:https://www.cnblogs.com/gkx0731/p/9745015.html
Copyright © 2011-2022 走看看