首页
登录 | 注册

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)测试结果:

  python之协程

  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()

  效果:

  python之协程

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

  解释:

  这里的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()

  效果:

  python之协程

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

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

  这里客户端使用telnet测试。

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

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

 


相关文章

  • Python是什么? Python 是一种面向对象的解释型计算机程序设计语言,由荷兰人Guido van Rossum于1989年发明,第一个公开发行版发行于1991年. Python是纯粹的自由软件, 源代码和解释器CPython遵循 G ...
  • 定义函数时,默认参数必须指向不变的对象 参数为可变对象时,正常调用的时候,结果没有问题,但是当使用默认参数的时候,结果就会和理想的有差距. In [78]: def add(L=[]): ...: L.append('END') ...: ...
  • 如何零基础开始自学Python编程
    转载——原作者:赛门喵 链接:https://www.zhihu.com/question/29138020/answer/141170242 0. 明确目标 我是真正零基础开始学Python的,从一开始的一窍不通,到3个月后成功搭建了一个 ...
  • 计算机基础 01 计算机基础之编程 02 计算机组成原理 03 计算机操作系统 04 编程语言分类 Python解释器 05 Python和Python解释器 06 执行Python程序的两种方式 07 Python集成开发环境 变量 08 ...
  • python接口自动化(二十一)--unittest简介(详解)
    简介 前边的随笔主要介绍的requests模块的有关知识个内容,接下来看一下python的单元测试框架unittest.熟悉 或者了解java 的小伙伴应该都清楚常见的单元测试框架 Junit 和 TestNG,这个招聘的需求上也是经常见到 ...
  • python接口自动化(二十三)--unittest断言——上(详解)
    简介 在测试用例中,执行完测试用例后,最后一步是判断测试结果是 pass 还是 fail,自动化测试脚本里面一般把这种生成测试结果的方法称为断言(assert).用 unittest 组件测试用例的时候,断言的方法还是很多的,下面介绍几种常 ...

2019 cecdns.com webmaster#cecdns.com
12 q. 0.075 s.
京ICP备10005923号