• python之路 -- 协程


      单线程下,我们不可避免程序中出现io操作,但如果我们能在自己的程序中(即用户程序级别,而非操作系统级别)控制单线程下的多个任务能在一个任务遇到io阻塞时就切换到另外一个任务去计算,这样就保证了该线程能够最大限度地处于就绪态,即随时都可以被cpu执行的状态,相当于我们在用户程序级别将自己的io操作最大限度地隐藏起来,从而可以迷惑操作系统,让其看到:该线程好像是一直在计算,io比较少,从而更多的将cpu的执行权限分配给我们的线程。

    1.协程

    --本质上是一个线程

    --能够在多个任务之间切换来节省一些IO时间

    --协程中任务之间的切换也消耗时间,但是开销要远远小于进程线程之间的切换

    2.用yield实现任务的切换

    --yiled可以保存状态,yield的状态保存与操作系统的保存线程状态很像,但是yield是代码级别控制的,更轻量级

    --send可以把一个函数的结果传给另外一个函数,以此实现单线程内程序之间的切换

    import time
    def consumer():
        while True:
            x = yield
            time.sleep(1)
            print('处理了数据 :',x)
    
    def producer():
        c = consumer()
        next(c)
        for i in range(10):
            time.sleep(1)
            print('生产了数据 :',i)
            c.send(i)
    
    producer()

    3.greenlet模块实现协程

    from greenlet import greenlet
    def eat():
        print('eating start')
        g2.switch()           
        # 转换到函数play并保存执行进度,当再转换到此函数时,接着此处执行
        print('eating end')
        g2.switch()
    
    def play():
        print('playing start')
        g1.switch()
        print('playing end')
    
    g1 = greenlet(eat)    
    # 将eat函数交给greenlet执行,赋给g1,当调用g1.switch()时就去执行eat函数
    g2 = greenlet(play)
    g1.switch()   # 执行函数eat
    
    """执行结果为:
    eating start
    playing start
    eating end
    playing end
    """

    4. gevent模块实现协程

    from gevent import monkey;monkey.patch_all()
    # 这句代码的作用就是让协程的这个模块认识其他模块中的IO操作,然后再执行过程中遇到IO操作进行跳转。必须在第一句
    import gevent
    import time
    def eat():
        print('eating start')
        time.sleep(1)
        print('eating end')
    
    def play():
        print("playing start")
        time.sleep(1)
        print('playing end')
    
    g1 = gevent.spawn(eat)
    g2 = gevent.spawn(play)
    g1.join()
    g2.join()
    
    """执行结果为:
    eating start
    playing start
    eating end
    playing end
    """

    # 进程和线程的任务切换由操作系统完成

    # 协程任务之间的切换由程序(代码)完成,只有遇到协程模块能识别的IO操作的时候,程序才会进行任务切换,实现并发的效果

     同步 和 异步
    from gevent import monkey
    monkey.patch_all()
    import time
    import gevent
    
    def task(n):
        time.sleep(0.5)
        print(n)
    
    def sync():
        for i in range(10):
            task(i)
    
    def async():
        g_lst = []
        for i in range(10):
            g = gevent.spawn(task,i)
            g_lst.append(g)
        gevent.joinall(g_lst)  # for g in g_lst:g.join()
    
    sync()  # 同步
    async() # 异步

    协程 : 能够在一个线程中实现并发效果的概念

       能够规避一些任务中的IO操作

       在任务的执行过程中,检测到IO就切换到其他任务

       协程 在一个线程上 提高CPU 的利用率

       协程相比于多线程的优势 切换的效率更快

    # 爬虫的例子
    # 请求过程中的IO等待
    from gevent import monkey;monkey.patch_all()
    import gevent
    from urllib.request import urlopen    # 内置的模块
    def get_url(url):
        response = urlopen(url)
        content = response.read().decode('utf-8')
        return len(content)
    
    g1 = gevent.spawn(get_url,'http://www.baidu.com')
    g2 = gevent.spawn(get_url,'http://www.sogou.com')
    g3 = gevent.spawn(get_url,'http://www.taobao.com')
    g4 = gevent.spawn(get_url,'http://www.hao123.com')
    g5 = gevent.spawn(get_url,'http://www.cnblogs.com')
    gevent.joinall([g1,g2,g3,g4,g5])
    print(g1.value)
    print(g2.value)
    print(g3.value)
    print(g4.value)
    print(g5.value)
    
    # 几乎同时得到输出结果
    爬虫的例子

    5.协程实现socketserver

      server端

    from gevent import monkey
    monkey.patch_all()
    import socket
    import gevent
    
    sk = socket.socket()
    sk.bind(('127.0.0.1',8080))
    sk.listen()
    
    def talk(conn):
        conn.send(b'hello')
        print(conn.recv(1024).decode('utf-8'))
        conn.close()
    
    while True:
        conn,addr = sk.accept()
        gevent.spawn(talk,conn)
    
    sk.close()
     
    View Code

      client端

    import socket
    sk = socket.socket()
    sk.connect(('127.0.0.1',8080))
    
    msg = sk.recv(1024).decode('utf-8')
    print(msg)
    info = input('>>>').encode('utf-8')
    sk.send(info)
    sk.close()
    View Code
  • 相关阅读:
    Android学习笔记之-Websercive 通讯
    Android学习笔记
    Android学习笔记-Android生成数字证书+签名
    jQuery教程总结
    SQL 数据库备份和恢复 镜像配置(证书方式)
    【网络部分总结的很好的帖子】方便以后找
    【动态规划】最长递增子序列
    【美团~牛客】十六进制转十进制
    【二分查找】及相关问题
    【动态规划】
  • 原文地址:https://www.cnblogs.com/aberwang/p/9466603.html
Copyright © 2020-2023  润新知