• python_协程


    协程

    问题一: 生成器与函数的区别?
    生成器分阶段的返回多个值,相当于有多个出口(结果); yield

    '''
    yield  # 中断、返回函数值
    1、只能在函数中使用
    2、会暂停函数执行并且返回表达式结果
    3、一次只能返回一个值
    
    yield 取值
    next()
    '''
    def func():
        print(1)
        yield 'a'
        print(2)
        yield 'b'
        print(3)
    
    g = func()
    print(next(g))
    g.__next__()

    问题二: 协程与生成器的区别?
    有多个出口,同时可以有多个入口

    def func():
        a = (yield )
        print(a)
        b = (yield )
        print(b)
        # c = (yield )
        # print(c)
    
    g = func()
    g.send(None)   # 第一次发送必须是None  或者使用 next(g)    对应的 a,发送的是个空
    print(g.send(1))    # 对应的是b
    print(g.send(2))   # 到这里就停止了。   StopIteration, 因为后面没有了

    问题三: 协程算并发嘛?
    严格来说 不算。如果有一个地方卡住了, 会一直卡
    问题四: 协程的意义?
    最主要"配合io多路复用使用",当前的意义就是切换使用,耗费资源小

    生产者/消费者模型

    import random, time
    
    # 生产者
    def produce(consumer):  # 参数(生成器)
        next(consumer)
        while True:
            item = random.randint(0, 99)    # 随机生成一个数
            print("生产者生产了%s" % item)
            consumer.send(item)     # 把item给消费者
            time.sleep(2)
    
    def consumer():     # 消费者 才是一个协程(生成器)
        while True:
            item = (yield )
            print("消费者消费了%s " % item)
    
    c = consumer()
    produce(c)

    greenlet

    问题一: 什么是greenlet ?
    虽然CPython(标准Python)能够通过生成器来实现协程,
    但使用起来还并不是很方便。 与此同时,Python的一个衍生版 Stackless Python
    实现了原生的协程,它更利于使用。 于是,大家开始将 Stackless 中关于协程的代码 单独拿出来做成了CPython的扩展包。 这就是 greenlet 的由来,因此 greenlet 是底层实现了原生协程的 C扩展库。
    说白了就是协程
    问题二: 如何使用 greenlet ?

    from greenlet import greenlet
    import random, time
    
    # 生产者
    def produce():  # 参数(生成器)
        while True:
            item = random.randint(0, 99)    # 随机生成一个数
            print("生产者生产了%s" % item)
            c.switch(item)     # 切换到消费者,并将item传入消费者
            time.sleep(2)
    
    def consumer():     # 消费者 才是一个协程(生成器)
        while True:
            item = p.switch()       # 切换到消费者,并等待消费者传入item
            print("消费者消费了%s " % item)
    
    p = greenlet(produce)  # 把函数包装成一个协程
    c = greenlet(consumer)
    c.switch()  # 启动

    问题三: 为什么需要greenlet ?
    greenlet 的价值
      价值一: 高性能的原生协程
      价值二: 语义更加明确的显式切换
      价值二: 语义更加明确的显式切换

    gevent协程

    问题一: 什么是 gevent ?

    sudo pip3 install gevent

    虽然,我们有了 基于 epoll 的回调式编程模式,但是却难以使用。

    即使我们可以通过配合 生成器协程 进行复杂的封装,以简化编程难度。 但是仍然有一个大的问题: 封装难度大,现有代码几乎完全要重写

    gevent,通过封装了 libev(基于epoll) 和 greenlet 两个库。 帮我们做好封装,允许我们以类似于线程的方式使用协程。

    以至于我们几乎不用重写原来的代码就能充分利用 epoll 和 协程 威力。

    问题二: gevent 的价值是什么 ?
    遇到阻塞就切换到 另一个协程继续执行 !
    价值一: 使用基于 epoll 的 libev 来避开阻塞
    价值二: 使用基于 gevent 的 高效协程 来切换执行
    价值三: 只在遇到阻塞的时候切换, 没有轮需的开销,也没有线程的开销


    问题三: 如何使用 gevent ?
    g e v e n t 并 发 服 务 器

    from gevent import monkey;monkey.patch_socket()
    
    import gevent
    import socket
    
    server = socket.socket()
    server.bind(('0.0.0.0', 8888))
    server.listen(1000)
    
    def workon(conn):
        while True:
            data = conn.recv(1024)
            if data:
                print(data.decode())
                conn.send(data)
    
            else:
                conn.close()
                break
    while True:
        conn, addr = server.accept()
        # Thread(target=workon, args=(conn,)).start()   # 多线程的方法
        gevent.spawn(workon, conn)

    gevent 协程通信

    gevent通信 问题引入
    问题一: 协程之间不是能通过switch通信嘛?
    是的,由于 gevent 基于 greenlet,所以可以。

    问题二: 那为什么还要考虑通信问题?
    因为 gevent 不需要我们使用手动切换, 而是遇到阻塞就切换,因此我们不会去使用switch !

    gevent.queue.Queue
    from gevent import monkey;monkey.patch_all()
    import gevent
    from gevent.queue import Queue
    import random
    '''
    gevent.queue.Queue
    '''
    q = Queue(3)
    # 生产者
    def produce(q):
        while True:
            item = random.randint(0, 99)
            print("生产者生产了%s" % item)
            q.put(item)
            gevent.sleep(1)
    
    def consumer(q):
        while True:
            item = q.get()
            print("消费者消费了%s" % item)
            gevent.sleep(1)
    
    p = gevent.spawn(produce, q)    # 将函数封装成协程,并开始调度
    c = gevent.spawn(consumer, q)
    # c.switch()   # 开启(不使用)
    
    gevent.joinall([p, c])   # 阻塞(一阻塞就切换协程)等待

    测试用客户端:

    import socket
    
    client = socket.socket()                    #创建一个套接字
    client.connect(   ('127.0.0.1',8888  ) )   #连接服务端
    
    while True:
        data = input('请输入你要发送的数据:')
    
        data = data.encode()
    
        client.send(data)
        print('接收到的数据:', client.recv(1024).decode())

    协程整理完毕。

    作者:含笑半步颠√

    博客链接:https://www.cnblogs.com/lixy-88428977

    声明:本文为博主学习感悟总结,水平有限,如果不当,欢迎指正。如果您认为还不错,欢迎转载。转载与引用请注明作者及出处。

  • 相关阅读:
    给TextView添加超链接的四种方式
    详解ExplosionField的使用,实现View的粉碎效果
    SpannableString使用详解
    android开发之wheel控件使用详解
    使用HttpURLConnection实现在android客户端和服务器之间传递对象
    关于Fragment与Fragment、Activity通信的四种方式
    Volley完全解析
    ListView异步加载图片,完美实现图文混排
    使用DrawerLayout实现QQ5.0侧拉菜单效果
    使用DrawerLayout实现侧拉菜单
  • 原文地址:https://www.cnblogs.com/lixy-88428977/p/9660613.html
Copyright © 2020-2023  润新知