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

      一、这几天在折腾Python的协程问题,Python的协程相对原理上来说要简单很多了。在使用Java做开发的时候,经常使用线程,过程中经常也有听说过微线程/协程的概念,但是没有去深刻的学习过Java的协程是怎么实现的。这一篇文章主要是讲述Python中的协程原理。后面补上Java中的协程原理!

      二、前面我们基本大概了解了多线程和多进程的问题,在资源的占用上面,多进程比多线程占用的多。我们这这里通过cpu调度最小的单位线程来说:

      1)多线程:多线程的调用需要cpu分配时间片然后来回的切换,并且是随机的。

      2)协程:协程是在单线程中的多个任务同时执行的相互配合,避免了线程之间切换造成的资源浪费

      3)意义:当我们在使用多线程的时候,如果存在长时间的I/O操作。这个时候线程一直处于阻塞状态,如果线程很多的时候,会存在很多线程处于空闲状态,造成了资源应用不彻底。相对的协程不一样了,在单线程中多个任务来回自行如果出现长时间的I/O操作,让其让出目前的协程调度,执行下一个任务。当然可能所有任务,全部卡在同一个点上,但是这只是针对于单线程而言,当所有数据正常返回时,会同时处理当前的I/O操作。

      4)作用:用更小的资源做更多地操作,更加高效的利用线程。当然,也有问题点,如果存在计算密集型的操作,协程起到的效果不是那么大。

      5)理解:协程不是真正意义上的像线程一样开辟空间(比如一个线程在64位系统下,stack大小为1024KB),协程没有,只是在单线程中,多个任务来回切换造成的多任务执行的假象。

      三、实现原理

      1)基本原理:是用过yield的方式来实现,来阻塞当前执行,等到下一个next()来了过后接着执行。

      yield:生成器的关键字,通过暂停并返回数据,达到产生数据的效果。

      2)代码理解:

    import time
    
    def show_a():
        while True:
            print "---A---"
            yield
            time.sleep(0.5)
    
    def show_b(c):
        while True:
            print "---B---"
            c.next()
            time.sleep(0.5)
    
    if __name__ == '__main__':
        # 生成器
        a = show_a()
        # 调用方法show_b
        show_b(a)

      3)测试结果:

      

      4)解释:

      a、协程是通过生成器的方式来达到效果的。

      b、目的让多个任务来回/同时执行

      四、通过greenlet实现协程

    from greenlet import greenlet
    
    def test1():
        while True:
            print "----A----"
            # 切换
            gr2.switch()
    
    def test2():
        while True:
            print "----B----"
            # 切换
            gr1.switch()
    
    gr1 = greenlet(test1)
    gr2 = greenlet(test2)
    
    if __name__ == '__main__':
        gr1.switch()

      效果:

      

      这样达到的效果就是多任务执行

      解释:

      这里的switch()方法就是yield的效果

      五、通过gevent达到协程的效果,这里通过socket连接阻塞的例子来演示:

    from gevent import socket, monkey
    
    # 用于动态加入yield
    monkey.patch_all()
    
    def request_handle(cli, address):
        while True:
            # 等待接收数据,阻塞
            data = cli.recv(1024)
            if len(data) > 0:
                print data
            else:
                print "close" + str(address)
                cli.close
                break
    
    def server(port):
        s = socket.socket()
        s.bind(('', port))
        s.listen(5)
        while True:
            # 等待接入,阻塞
            cli, address = s.accept()
            print address
            # 新建一个协程
            gevent.spawn(request_handle, cli, address)
    
    def server_test():
        while True:
            print "test"
            gevent.sleep(1)
    
    if __name__ == '__main__':
        # 为了独立运行,加入2个协程任务
        s1 = gevent.spawn(server, 9000)
        # 用于展示独立性
        s2 = gevent.spawn(server_test)
        s1.join()
        s2.join()

      效果:

      

      注意:这里的yield是自动加入的,目的是自动让出时间执行过长的任务,到可以执行的任务中区执行。

      阻塞为一种长时间的等待,当有新的连接进来,才会执行请求任务。

      这里客户端使用telnet测试。

      六、总结:Python的协程原理是通过yield关键字,并且通过生成器的方式来达到多任务的执行。对于I/O密集型的操作,可以采用协程方式来做。如果存在计算密集型(占用cpu的操作很高),不要使用协程,最好使用多进程。

    个人对于协程的理解,基本上就是这些,如果存在错误的地方还请指出。

  • 相关阅读:
    IFNULL和isnull用法
    Python 进制转换 二进制 八进制 十进制 十六进制
    xhr是什么文件类型?
    from __future__ import unicode_literals
    sort is deprecated, use sort_values(inplace=True) for INPLACE sorting
    Autodesk View and Data API二次开发学习指南
    设置Mac 中保存对话框默认为扩展窗口
    [大数据学习研究] 错误排查,Hadoop集群部分DataNode不能启动
    IDEA 环境下更改Maven的仓库镜像提高下载速度
    [大数据学习研究] 4. Zookeeper-分布式服务的协同管理神器
  • 原文地址:https://www.cnblogs.com/ll409546297/p/10944450.html
Copyright © 2011-2022 走看看