`
frank-liu
  • 浏览: 1684153 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

python coroutine的学习和总结

阅读更多

简介

    因为最近一段时间需要研究一些openstack相关的东西,在阅读一些相关代码的时候碰到很多python特定的一些特性,比如generator, coroutine以及一些相关的类库,比如eventlet, greenlet。在openstack里引用的第三方类库非常多,这些特性和类库看起来还比较复杂。如果需要对openstack里面某些特性的实现非常熟悉的话,就需要对这些牵涉到的基础的东西有个很好的了解。这里就针对coroutine的特性和它的使用做一个总结。

 

coroutine的定义和使用

    在前面一篇关于generator的文章里,我提到了怎么定义和使用generator。当时我们使用yield value或者和list comprehension类似的语法来定义generator。我们返回的generator其实是一个可以不断取得数据的集合,所以一般使用它们的代码一般是一个循环。在这种情况下,我们感觉像是首先定义了一组管道,然后在真正需要的时候才在代码里去提取它们。

    实际上,coroutine和generator还是很有关系的,我们来看如下的代码:

def grep(pattern):
    print "Looking for %s" % pattern
    while True:
        line = (yield)
        if pattern in line:
            print line,

    这部分代码看起来和generator很像,不过又不同。这里有一个line = (yield) 的语句。而在generator里,我们是需要yield value来返回值的。而这里后面根据获得的值还可以打印出来了。我们使用它们的代码如下:

if __name__ == '__main__':
    g = grep("python")
    g.next()
    g.send("Yeah, but no, but yeah, but no")
    g.send("A series of tubes")
    g.send("python generators rock!")

    这里,我们定义了方法grep,然后调用一个send方法。代码执行的输出结果如下:

Looking for python
python generators rock!

    结合前面代码的部分,看起来好像是yield部分后面会针对包含有python这个串的字符进行处理。而没有的则不会处理。看来这个yield像是有什么玄机,看起来不简单。

    实际上,我们在使用line = (yield)这部分就是定义了一个coroutine。coroutine是什么呢?coroutine可以说是一种实现协作式编程的手法。它可以设置有多个入口点和恢复执行的点,可以实现一些执行流程的转移。这部分概念看起来有点难懂。我们以前面的这部分代码为例来分析一下想关的概念。

    在我们代码中,定义的yield这个部分相当于等待接收数据。所以在没有数据到来的时候,它就相当于被阻塞,等在那里。而为了触发这个部分,我们在使用的代码里首先用g.next()来初始化它。然后通过g.send("")方法将数据发送给它。这样,yield返回的就是send方法里带的参数了。然后我们可以接着在循环部分来处理它。这样看来,coroutine更多的是一个数据消费者的角色。每次都是等数据过来,来了之后就通过yield部分返回,然后处理。否则就等在那里。

 

coroutine的几个应用

    因为coroutine相当于一个数据的消费者,我们可以做一种这样的应用。首先是一个生产者将一些数据准备好,然后将数据发送给一个coroutine来处理。我们来看一个文件处理的示例:

 

import time
def follow(thefile, target):
    thefile.seek(0,2)      # Go to the end of the file
    while True:
         line = thefile.readline()
         if not line:
             time.sleep(0.1)    # Sleep briefly
             continue
         target.send(line)
     这里是一个读取文件,然后将文件内容通过target发送到target来处理的过程。所以我们这里要做的就是将target作为一个参数传入给follow方法。当然,target在这里必须是一个coroutine,它来接收和处理这些数据。target的定义如下:

 

 

@coroutine
def printer():
    while True:
         line = (yield)
         print line,
    这里用到了yield,然后就可以直接将接收到的数据打印出来了。当然,还有一个值得注意的地方是用到了一个@coroutine的decorator。因为在每次使用coroutine之前我们需要调用一次target.next()或者target.send(None)来初始化它。这样我们使用的时候很容易忘记这一步,一种办法就是定义好一个这样的decorator,然后每次将这个decorator加上就保证这一步被执行了。@coroutine decorator的定义如下:

 

 

def coroutine(func):
    def start(*args,**kwargs):
        cr = func(*args,**kwargs)
        cr.next()
        return cr
    return start
    对于这部分decorator理解如果有问题的话可以参考我decorator相关的这篇文章。我们最终使用前面这部分的代码如下:

 

 

if __name__ == '__main__':
    f = open("access-log")
    follow(f,printer())
    当然,这个示例主要讲的是使用一个coroutine来处理一个传递过来的消息。如果我们要构造类似于pipeline的东西,可以将一个coroutine同时当作数据处理的部分,也可以当作数据传递的部分,比如看如下的代码:

 

 

@coroutine
def grep(pattern,target):
    while True:
        line = (yield)           # Receive a line
        if pattern in line:
            target.send(line)    # Send to next stage
    这里我们定义的grep方法在接收到数据之后,相当于做了一个判断,如果pattern在传入的line中间,则将这个数据传递给下一个coroutine处理。这就实现了一个pipeline的雏形。当然,除了这种传输的示例,我们也可以将消息传递给多个coroutine。这些示例我们可以参考后面的一些资料去做详细的分析。

 

 

关于控制传递

    在wiki上关于coroutine的介绍,引用了经典的producer-consumer问题。以前针对这个问题,更多的是针对多线程的producer-consumer问题处理。在那种场景下,我们需要通过锁或者某些互斥变量来实现对队列元素的处理。而实际上,用coroutine来解决这个问题也是一种很理想的方法。在wiki上给出的伪代码是这样的:

 

coroutine produce
    loop
        while q is not full
            create some new items
            add the items to q
        yield to consume
 
coroutine consume
    loop
        while q is not empty
            remove some items from q
            use the items
        yield to produce
    这些代码里最有意思的地方就是,我们完全可以用python coroutine的方式来做一个实现。比如说,我们可以将queue里元素的个数作为传递的参数。作为consumer,可以采用这样的方式来写:

 

    

@coroutine
def consumer(target):
    while True:
        items = (yield)
        if items > 0:
            remove item from queue
            items -= 1
        target.send(items)
     作为producer也可以采用类似的方法来写。这里就不再赘述了。当然,还有一个要注意的地方就是,这里虽然实现了控制的转移,但是并没有牵涉到线程的变换,这里所有的一切其实还是在同一个进程里执行的。只不过这种控制转移的方式在很多地方有比较高效率的应用,它在实现事件循环和分发、非阻塞IO访问方面有很多的应用。我也会在后续的一些文章里针对它们的一些应用做进一步的分析。

 

 

总结

    coroutine是一个实现多个任务之间互相切换的手段,它相当于一种将一个当前执行的结果传递给另外一个过程。和generator的使用过程比起来,它更像是一种“推”模式。因为我们要使用一个coroutine的时候,必然是需要有其它的过程send数据过来。因为yield的过程有点类似于一个操作系统里中断的概念,它相当于将一个进程的当前执行过程暂停,然后跳转到另外一个过程。这种过程和我们传统通过栈实现的子过程调用很不一样,所以表面上理解起来还是有点困难。

 

参考材料

 http://en.wikipedia.org/wiki/Coroutine

http://www.dabeaz.com/coroutines/

分享到:
评论

相关推荐

    基于python3.6 + tornado 实现简单的 RESTful API,用于机器学习模型部署和调用.zip

    总结,这个项目展示了如何利用Python 3.6和Tornado框架构建一个用于机器学习模型部署和调用的RESTful API,涉及到的主要知识点包括Python 3.6的新特性、Tornado的异步网络编程、RESTful API设计、模型部署以及性能...

    Python编程实战-运用设计模式、并发和程序库创建高质量程序.pdf

    Python提供了多种并发模型,如线程(Thread)、进程(Process)、协程(Coroutine)和异步I/O(例如asyncio库)。并发能够提高程序性能,但同时也引入了竞态条件和死锁等问题,因此,学习如何管理和调试并发代码是...

    Python 教程.zip

    3. 异步编程:Python提供了asyncio库支持异步I/O,通过协程(coroutine)实现非阻塞操作,适用于网络请求和高并发场景。 三、Python在特定领域的应用 1. Web开发:Django和Flask是两个流行的Python Web框架,它们提供...

    Stackless Python 并发式编程介绍.doc

    Stackless Python 是一种扩展了标准Python解释器的版本,它主要关注并发编程和微线程的实现。在标准Python中,每个线程都有自己的堆栈,这...通过学习Stackless Python,可以提升编程能力,更好地适应并发编程的挑战。

    Learning Concurrency in Python

    通过学习《Learning Concurrency in Python》这本书,读者不仅能够掌握Python中并发编程的基本原理和技术,还能了解到如何根据实际需求选择合适的并发策略。无论是对于初学者还是有一定经验的开发者来说,这都是一本...

    Stackless_Python并发式编程介绍[已校对版].pdf

    通过本文档的学习,读者不仅可以了解到Stackless Python的基本概念和核心功能,还能掌握如何利用这些工具来解决实际问题。Stackless Python为Python编程语言带来了更强大的并发能力,为开发者提供了一个更加高效、...

    Python100-master .zip

    《Python爬虫100题》是一份专为学习Python爬虫技术的实践教程,它包含了一系列精心设计的练习题目,旨在帮助初学者和进阶者深入理解和掌握Python网络爬虫的...在学习过程中,不断实践、总结和反思,是提升技能的关键。

    python 高级教程,不适合初学者

    4. **协程(Coroutine)**:协程是Python中并发执行的轻量级线程,它们可以并发地运行,并且可以由用户控制执行流程。通过`asyncio`库,我们可以编写非阻塞的异步代码,提高程序的响应速度和资源利用率。 5. **上...

    python进阶【绝对精品】.zip

    总结,"python进阶【绝对精品】.zip"是一个全面的Python进阶学习资源,它不仅包含理论知识,还有丰富的实践案例。不论你是想提升自己的Python技能,还是准备应对更具挑战性的项目,这份资料都将助你一臂之力。

    python英雄联盟皮肤爬取案例

    在本案例中,我们将深入探讨一个使用Python 3.11.7进行网络爬虫的实践项目,目标是抓取英雄联盟(League of Legends)...通过学习和实践这个案例,开发者不仅可以提升Python编程技能,还能对网络爬虫有更深入的理解。

    Python编写网页爬虫优化版

    Python 编写网页爬虫是数据获取的重要手段,特别是在大数据时代,从互联网上...在这个例子中,通过学习和实践,你将能够针对http://zuidazy.net电影网站定制一个高效、稳定的爬虫模型,进一步提升你的Python编程能力。

    Python异步编程|PySimpleGUI图形界面实例|PDF表格转换Excel文件

    在本主题中,我们将深入探讨Python的异步编程和如何使用PySimpleGUI创建图形用户界面(GUI)。...通过学习和实践这些技能,你可以创建出强大的数据处理工具,特别是在需要处理大量数据和交互式操作的场景下。

    Python语言多进程与多线程设计探究.zip

    总结,Python的多进程和多线程设计是提升程序效率的关键工具。理解它们的工作原理、优缺点以及如何选择和使用,将有助于编写出更高效、稳定的代码。通过深入学习"Python语言多进程与多线程设计探究"这份资料,开发者...

    Python Asyncio 库之从ChatGPT Bug了解Cancel机制

    总结起来,Python的Asyncio库通过协程和取消机制提供了一种高效的方式来管理并发任务。理解并熟练运用`asyncio.CancelledError`可以帮助我们编写更健壮、更响应的异步代码,特别是在处理像ChatGPT这样的实时交互系统...

    Python多线程、异步+多进程爬虫实现代码

    本文详细介绍了如何利用Python编写一个结合了多线程、异步和多进程技术的高效爬虫程序。通过这种方式,我们不仅可以大幅提高爬取速度,还能有效地应对网络延迟和服务器响应时间较长的问题。对于需要处理大规模数据集...

    PyPI 官网下载 | aiodine-1.1.0.tar.gz

    Python的异步编程模型是基于协程(coroutine)的概念,通过关键字`async`和`await`实现。`async def`定义了一个协程函数,`await`则在执行到该关键字时,会暂停当前函数的执行,等待另一个协程完成后再继续。asyncio...

    Python3多线程操作简单示例

    在Python3中,多线程是并发执行任务的一种方式,它可以提高程序的效率,尤其是在处理I/O密集型任务时。本文将深入探讨Python3中多线程的基本...学习并掌握这些技术,能够帮助我们编写出更加高效和灵活的Python程序。

    python协程之动态添加任务的方法

    ### Python协程之动态添加...通过本文的学习,我们了解到在Python中如何使用协程进行动态任务添加的方法。这种方法不仅能够提高程序的并发性能,还可以让程序结构更加清晰和灵活。希望本文能对你在实际开发中有所帮助。

    系统编程_网络编程_web编程.rar

    总结,这个压缩包提供的学习资源涵盖了Python系统编程、网络编程、Web编程和正则表达式的核心概念,对于提升Python开发者的技术水平有着极大的帮助。通过深入学习并实践这些内容,开发者可以构建出高效、稳定且功能...

Global site tag (gtag.js) - Google Analytics