zoukankan      html  css  js  c++  java
  • python 协程

    协程

    一、协程的本质:

    单线程实现并发,在应用程序里控制多个任务的切换+保存状态

    二、协程的目的:

    • 想要在单线程下实现并发
    • 并发指的是多个任务看起来是同时运行的
    • 并发=切换+保存状态

    三、补充:

    • yiled可以保存状态,yield的状态保存与操作系统的保存线程状态很像,但是yield是代码级别控制的,更轻量级
    • send可以把一个函数的结果传给另外一个函数,以此实现单线程内程序之间的切换
    • 如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制)

    四、优点

    • 应用程序级别速度要远远高于操作系统的切换

    五、缺点

    • 多个任务一旦有一个阻塞没有切,整个线程都阻塞在原地,该线程内的其他的任务都不能执行了
    • 一旦引入协程,就需要检测单线程下所有的IO行为,实现遇到IO就切换,少一个都不行,因为如果一个任务阻塞了,整个线程就阻塞了,其他的任务即便是可以计算,但是也无法运行了

    注意:单纯地切换反而会降低运行效率

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    #并发执行
    import time
     
    def producer():
        g=consumer()
        next(g)
        for i in range(100):
            g.send(i)
     
    def consumer():
        while True:
            res=yield
     
    start_time=time.time()
    producer()
    stop_time=time.time()
    print(stop_time-start_time)
     
    #串行
    import time
     
    def producer():
        res=[]
        for i in range(10000000):
            res.append(i)
        return res
     
     
    def consumer(res):
        pass
     
    start_time=time.time()
    res=producer()
    consumer(res)
    stop_time=time.time()
    print(stop_time-start_time)

    greenlet

    greenlet只是提供了一种比generator更加便捷的切换方式,当切到一个任务执行时如果遇到io,那就原地阻塞,仍然是没有解决遇到IO自动切换来提升效率的问题。

    注意:单纯的切换(在没有io的情况下或者没有重复开辟内存空间的操作),反而会降低程序的执行速度

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    #pip3 install greenlet
    from greenlet import greenlet
    import time
     
    def eat(name):
        print('%s eat 1' %name)
        time.sleep(2)
        g2.switch('tom')
        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('tom')
     
    """
    tom eat 1
    tom play 1
    tom eat 2
    tom play 2
    """

    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的返回值

    二、补充:

    • 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()放到文件的开头
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    #pip3 install gevent
    from gevent import monkey;monkey.patch_all()
    import gevent
    import time
     
    def eat(name):
        print('%s eat 1' % name)
        time.sleep(3)
        print('%s eat 2' % name)
     
    def play(name):
        print('%s play 1' % name)
        time.sleep(2)
        print('%s play 2' % name)
     
    start_time=time.time()
    g1=gevent.spawn(eat,'tom')
    g2=gevent.spawn(play,'rose')
     
    g1.join()
    g2.join()
    stop_time=time.time()
    print(stop_time-start_time)
    """
    tom eat 1
    rose play 1
    rose play 2
    tom eat 2
    3.003171920776367
    """
     
     
     
    from gevent import monkey;monkey.patch_all()
    import gevent
    import time
     
    def eat(name):
        print('%s eat 1' % name)
        time.sleep(3)
        print('%s eat 2' % name)
     
    def play(name):
        print('%s play 1' % name)
        time.sleep(2)
        print('%s play 2' % name)
     
    g1=gevent.spawn(eat,'tom')
    g2=gevent.spawn(play,'rose')
     
    # g1.join()
    # g2.join()
    gevent.joinall([g1,g2])

    三、通过gevent实现单线程下的socket并发

    from gevent import monkey;monkey.patch_all()一定要放到导入socket模块之前,否则gevent无法识别socket的阻塞

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    """
    服务端
    #基于gevent实现
    """
    from gevent import monkey,spawn;monkey.patch_all()
    from socket import *
     
    def communicate(conn):
        while True:
            try:
                data=conn.recv(1024)
                if not data:break
                conn.send(data.upper())
            except ConnectionResetError:
                break
     
        conn.close()
     
    def server(ip,port):
        server = socket(AF_INET, SOCK_STREAM)
        server.bind((ip,port))
        server.listen(5)
     
        while True:
            conn, addr = server.accept()
            spawn(communicate,conn)
     
        server.close()
     
    if __name__ == '__main__':
        g=spawn(server,'127.0.0.1',8090)
        g.join()
         
         
    """
    客户端
    """
    from socket import *
    from threading import Thread,currentThread
     
    def client():
        client=socket(AF_INET,SOCK_STREAM)
        client.connect(('127.0.0.1',8090))
     
        while True:
            client.send(('%s hello' %currentThread().getName()).encode('utf-8'))
            data=client.recv(1024)
            print(data.decode('utf-8'))
     
        client.close()
     
    if __name__ == '__main__':
        for i in range(500):
            t=Thread(target=client)
            t.start()
  • 相关阅读:
    mysql中的enum型
    mysql中的时间year/date/time/datetime
    一些数字的属性
    mysql增删
    Perl6 Bailador框架(8):自定义400/500
    react: typescript jest && enzyme
    webstorm tslint配置
    react: typescript import images alias
    JSONP原理及简单实现
    纯css画三角形
  • 原文地址:https://www.cnblogs.com/kingforn/p/11618298.html
Copyright © 2011-2022 走看看