zoukankan      html  css  js  c++  java
  • tornado用户指引(二)------------tornado协程实现原理和使用(一)

    摘要:Tornado建议使用协程来实现异步调用。协程使用python的yield关键字来继续或者暂停执行,而不用编写大量的callback函数来实现。(在linux基于epoll的异步调用中,我们需要自己显式的为异步执行结果安装大量的callback函数).协程的使用和编写异步代码一样简单,而且省去了线程的开销。协程使编写并发程序更加容易,而且没有上下文切换的开销。举例:
    from tornado import gen

    @gen.coroutine
      def fetch_coroutine(url)
    Tornado建议使用协程来实现异步调用。

    协程使用python的yield关键字来继续或者暂停执行,而不用编写大量的callback函数来实现。(在linux基于epoll的异步调用中,我们需要自己显式的为异步执行结果安装大量的callback函数).

    协程的使用和编写异步代码一样简单,而且省去了线程的开销。

    协程使编写并发程序更加容易,而且没有上下文切换的开销。

    举例:

    from tornado import gen
    @gen.coroutine
    def fetch_coroutine(url):
      http_client = AsyncHTTPClient()
      response = yield http_client.fetch(url)
    # 在python3.3以前的版本中, 在一个生成器函数中返回值是不允许的。你需要通过抛出异常的方式来达到相同的目的。
    return response.body

    @gen.coroutine是tornado实现的一个生成器,用来将fetch_coroutine函数包装为一个协程,可以把这个协程当作一个和线程等价的执行体。这个执行体会被tornado总的ioloop调用,并在需要阻塞时切换到其它协程执行,当阻塞调用完成时,ioloop会继续执行这个就绪的协程。通过这样的方式,可以在一个线程中执行多个协程,而且不会因为某个协程阻塞而阻塞其它就绪的协程。

    实现原理:

    包装后的函数会返回一个Future,当这个协程真正执行完成后,会设置Future的执行结果。可以通过yield来等待这个协程执行完成并得到结果。

    当协程执行yield http_client.fetch时,由于这个操作是一个网络I/O操作,属于阻塞操作。因此这个协程会暂停执行,进而tornado总的ioloop可以执行其它协程。

    在协程里面,yield语句阻塞的函数需要返回一个Future,这个Future代表了一个异步执行体,并在异步操作执行完成时设置Future的结果,tornado会在Future执行完成时,将Future的执行结果通过生成器的send方法将值传回yield语句,进而唤醒阻塞的协程继续运行。

    在实现上,http_client.fetch操作会通过将fd加入tornado ioloop的方式实现真正的异步操作,当fetch真正成功时,即epoll返回时,会设置Future的结果,并将此协程唤醒继续执行。

    综上所述,我们要利用tornado的协程功能,需要用@gen.coroutine包装我们的函数,并在需要阻塞的地方用yield语句阻塞。阻塞的代码需要返回一个Future,并通过某种异步方式将Future的执行结果设置好。

    使用tornado协程举例:

    1.我们可以在协程不执行任何阻塞操作,这样协程会一直执行直到完成:

    我们创建2个协程和一个总的协程,由于协程里没有阻塞操作,所以实际上两个协程是顺序执行完成的。

    from tornado import gen
    from tornado.ioloop import IOLoop

    @gen.coroutine
    def cor(n,str):
      for i in range(n):
        print(str,i)
      return 

    @gen.coroutine
    def main():
      cor(3,"first")
      cor(4,"second")
    IOLoop.instance().run_sync(main)

    2.有阻塞操作的协程: 我们在协程循环中增加了睡眠操作,这个sleep是tornado框架实现的,注意上面分析的过程,这个yield需要返回一个future. 另外,在main中我们也增加了yield操作,是因为要等待2个协程执行完再结束ioloop.否则程序会直接结束。 这样,可以看到2个协程交替执行,sleep操作并不会阻塞另外一个协程。

    from tornado import gen
    from tornado.ioloop import IOLoop

    @gen.coroutine
    def cor(n,str):
      for i in range(n):
      print(str,n)
      yield gen.sleep(1)
     return 

    @gen.coroutine
    def main():
      cor(3,"first")
      cor(3,"second")
      yield gen.sleep(3)
    IOLoop.instance().run_sync(main)

    3.如果把2中main函数代码改成下面这样,这样main协程就会等待第一个协程执行完,才会执行第2个协程。
    @gen.coroutine
    def main():
      yield cor(3,"first")
      yield cor(3,"second")
    IOLoop.instance().run_sync(main)

  • 相关阅读:
    TCP链接异常断开后,对端仍然ESTABLISH
    mds0: Many clients (191) failing to respond to cache pressure
    Linux C/C++编译过程中的各种not declared in this scope
    Centos7添加磁盘并分区格式化
    Windows Terminal——安装并配置主题
    Kbone——创建项目报错 name can no longer contain capital letters
    Redis——大批量删除redis的key
    Redis——设置最大内存 | key淘汰机制
    Nightingale——滴滴夜莺部署【一】
    ELK——使用Docker快速搭建
  • 原文地址:https://www.cnblogs.com/b02330224/p/10200902.html
Copyright © 2011-2022 走看看