第13章 asyncio模块

13 asyncio模块

13.1 事件循环
  • 大多数异步应用程序实现异步的基本机制 通过在后台执行的事件循环,当代码需要被执行时,才会注册到循环中。 把一个函数注册到事件循环会导致它变成一个任务,事件循环负责在获得任务后,马上执行它。另一种方式是事件循环有时在等待一定时间后,再执行任务。

    一个简单的事件循环
    import asyncio
    
    loop = asyncio.get_event_loop()
    print(loop.is_running()) # False
    
    # 执行循环
    # loop.run_forever()
    
    # 注册任务并执行循环
    import functools
    
    def hello_world():
        print('hello world!')
    
    def stop_loop(loop):
        print('stopping loop!')
        loop.stop()
    
    loop.call_soon(hello_world)
    
    # partial 偏函数
    loop.call_soon(functools.partial(stop_loop, loop))
    
    loop.run_forever()
    
    # 延迟调用 单位秒
    loop.call_later(10, hello_world)
    loop.call_later(12, functools.partial(stop_loop, loop))
    loop.run_forever()
    
    • 大多数接受函数的asyncio 方法仅仅接受函数对象(或其他被调用元素)但这些函数在被调用时并没有带参数。partial方法本身接受参数与关键字参数,这些参数在底层函数被调用时被传给底层函数。

      def hello_world():
          print(" Hello world!")
      ## 等价于
      functools.partial(print, 'Hello world!')
      
  • 任务结束之前,一直执行循环

    • @asyncio.coroutine将普通的Python函数转换成一个协程,当调用run_until_complete时,会将任务注册并在任务结束前执行循环
    @asyncio.coroutine
    def trivial():
        return 'Hello world!'
    
    loop.run_until_complete(trivial())
    
  • 执行一个后台循环

    import asyncio
    import threading
    import functools
    
    def run_loop_forever_in_background(loop):
        def thread_func(l):
            asyncio.set_event_loop(l)
            l.run_forever()
        thread = threading.Thread(target=thread_func, args=(loop, ))
        thread.start()
        return thread
    
    loop = asyncio.get_event_loop()
    run_loop_forever_in_background(loop)
    
    loop.call_soon_threadsafe(functools.partial(print, 'Hello world!'))
    
13.2 协程
  • 协程是一种用于在事件循环中执行的特殊函数;如果创建了协程并未执行它,日志记录中会出错

    import asyncio
    @asyncio.coroutine
    def coro_sum(*args):
        answer = 0
        for i in args:
            answer += i
        return answer
    
    loop = asyncio.get_event_loop()
    print(loop.run_until_complete(coro_sum(1,2,3,4,5)))
    
  • 嵌套

    • outer的协程使用yield from语法调用名称 nestedde 协程;outer的协程遇到yield from语句是会挂起。nested协程被放入事件循环并执行,outer协程在nested完成并返回结果之前不会继续执行。

      import asyncio
      @asyncio.coroutine
      def nested(*args):
          print('The `nested` function ran with args: %r'%(args,))
          return [i+1 for i in args]
      
      @asyncio.coroutine
      def outer(*args):
          print('The `outer` function ran with args: %r'%(args,))
          answer = yield from nested(*[i*2 for i in args])
          return answer
      
      loop = asyncio.get_event_loop()
      print(loop.run_until_complete(outer(2, 3, 5, 8)))
      
      ''''''
      The `outer` function ran with args: (2, 3, 5, 8)
      The `nested` function ran with args: (4, 6, 10, 16)
      [5, 7, 11, 17]
      
13.3 Future对象和 Task对象
  • Future对象: 用于通知异步函数状态的对象,包括函数的 执行、 完成、 取消、函数的结果或者函数引发异常,返回对应的异常和回溯。

  • Task对象是 Future对象的子类,每一个协程在事件循环中被安排执行后,协程就会被Task对象包装。当你调用run_until_complete并传递一个协程时,该协程会被包装到一个Task对象中并执行。Task对象的任务是存储结果并为``yield from语句 提供值。

  • asyncio.async 将协程放入事件循环,并返回对应的Task对象。

    import asyncio
    
    @asyncio.coroutine
    async def make_tea(variety):
        print("Now making %s tea."%variety)
        asyncio.get_event_loop().stop()  # 
        return '%s tea'%variety
    
    loop = asyncio.get_event_loop()
    # 注册事件循环
    task = loop.create_task(make_tea('chamomile'))
    loop.run_forever()  #Now making chamomile tea.
    print(task.done())  #True
    print(task.result())    #chamomile tea
    
    task = [loop.create_task(make_tea('chamomile'))]
    loop.run_until_complete(asyncio.wait(task))
    print(task[0].done())  #True
    print(task[0].result()) #chamomile tea
    
  • 回调

    import asyncio
    
    @asyncio.coroutine
    async def make_tea(variety):
        print("Now making %s tea."%variety)
        return '%s tea'%variety
    
    def confirm_tea(future):
        print('The %s is made.'%future.result())
    
    loop = asyncio.get_event_loop()
    # 注册事件循环
    task = loop.create_task(make_tea('green'))
    # 将confirm_tea方法作为回调赋值给task的地方;相当于调用 confirme_tea(task)?
    task.add_done_callback(confirm_tea)
    loop.run_until_complete(task)
    
  • 带参数的回调

    import asyncio
    import functools
    @asyncio.coroutine
    async def make_tea(variety):
        print("Now making %s tea."%variety)
        return '%s tea'%variety
    
    def confirm_tea(ingredient, future):
        print('Now adding %s to the %s.'%(ingredient, future.result()))
    
    loop = asyncio.get_event_loop()
    # 注册事件循环
    task = loop.create_task(make_tea('green'))
    # 增加偏函数 设定默认值
    task.add_done_callback(functools.partial(confirm_tea, 'honey'))
    
    loop.run_until_complete(task)
    
13.5 任务聚合
  • 聚集任务:聚集任务目的提供的第一种机制是通过 gather函数;gather函数接受一系列协程或任务,并返回将那些任务聚合后的单个任务

    import asyncio
    loop = asyncio.get_event_loop()
    
    @asyncio.coroutine
    def make_tea(variety):
        print("Now making %s tea."%variety)
        return '%s tea'%variety
    
    def mix(future):
        print("Mixing the %s together."%' and '.join(future.result()))
    
    meta_task = asyncio.gather(
        make_tea('chamomile'),
        make_tea('green'),
        make_tea('herbal')
    )
    meta_task.add_done_callback(mix)
    loop.run_until_complete(meta_task)
    
  • 等待任务asyncio.wait 协程接受一系列协程或任务,一旦完成就返回结果。注意该协程的签名与asyncio.gather不同。 每个协程或任务都是gather的一个单独位置参数,而wait接受一个列表作为参数。此外,wait接受一个用于在任何任务完成后返回的参数,而无需等待所有任务完成。wait返回两部分:第一个元素是已完成的Future对象,第二个元素为未完成的部分。

    import asyncio
    loop = asyncio.get_event_loop()
    
    @asyncio.coroutine
    def make_tea(variety):
        print("Now making %s tea." % variety)
        return '%s tea'%variety
    
    coro = asyncio.wait([
        make_tea('chamomile'),
        make_tea('green'),
        make_tea('herbal')])
    
    loop.run_until_complete(coro)
    
    • 超时

      import time
      import asyncio
      loop = asyncio.get_event_loop()
      
      coro = asyncio.wait([
          asyncio.sleep(5),
          asyncio.sleep(3)],
          timeout=3
      )
      t1 = time.time()
      print(loop.run_until_complete(coro))
      t2 = time.time()
      print('time:%s'%(t2-t1))
      
    • 等待任意任务

      import time
      import asyncio
      loop = asyncio.get_event_loop()
          
          coro = asyncio.wait([
              asyncio.sleep(5),
              asyncio.sleep(3)],
              return_when=asyncio.FIRST_COMPLETED  # 等待任意任务完成
          )
          t1 = time.time()
          loop.run_until_complete(coro)
          t2 = time.time()
          print('time:%s'%(t2-t1))
      
    • 等待异常

      import asyncio
      loop = asyncio.get_event_loop()
      
      @asyncio.coroutine
      def raise_ex_after(seconds):
          yield from asyncio.sleep(seconds)
          raise RuntimeError('Raising an exception.')
      
      coro = asyncio.wait(
          [asyncio.sleep(1),
           raise_ex_after(2),
           asyncio.sleep(3)],
          return_when=asyncio.FIRST_EXCEPTION  # 等待抛出异常结束
      )
      
      t1 = time.time()
      loop.run_until_complete(coro)
      t2 = time.time()
      print('time:%s'%(t2-t1))
      
  • 队列

    • 全功能队列应用库celery

    • asyncio 模块提供队列仅仅是基本的队列; Queue类提供的方法被用于顺序或一部上下文中

    • 非常直接的先进先出(FIFO)队列;put_nowait和get_nowait 添加或移除项;

      import asyncio
      queue = asyncio.Queue()
      queue.put_nowait('foo')
      print(queue.qsize())    # 1
      print(queue.get_nowait())   # foo
      print(queue.qsize())    # 0
      
    • Queue类 提供一个名称为 get的方法,get方法在队列为空时不会引发异常,而是等待 项 被添加到队列中, 然后再从队列中获得该项并立刻返回。

      import asyncio
      loop = asyncio.get_event_loop()
      queue = asyncio.Queue()
      queue.put_nowait('foo')
      print(loop.run_until_complete(queue.get()))
      
      import asyncio
      loop = asyncio.get_event_loop()
      queue = asyncio.Queue(maxsize=5)	# 设置最大队列长度
      task = loop.create_task(queue.get())
      coro = asyncio.wait([task], timeout=1)
      print(loop.run_until_complete(coro))  # 因为队列为空,处于阻塞状态
      print(task.done())  # False
      queue.put_nowait('bar')
      
      import functools
      def stop(l, future):
          l.stop()
      
      task.add_done_callback(functools.partial(stop, loop))
      loop.run_forever()
      print(task.done())  # True
      print(task.result())    # bar
      
  • 服务器

    • asyncio 模块最常见的用处是创建一个座位守护程序的服务并接受命令。asyncio模块 定义了一个Protocol类,用于在接受或失去连接时以接受到数据时触发对应事件。 此外,事件循环定义了一个create_server 方法,用于打开一个 socket连接,该连接允许将数据发送到事件循环并交给Protocol类。

    • '''
      在命令行执行: telnet 127.0.0.1 8000
      '''
      import asyncio
      from asyncio import transports
      
      class Shutdown(Exception):
          pass
      
      class ServerProtocol(asyncio.Protocol):
          def connection_made(self, transport: transports.BaseTransport) -> None:
              self.transport = transport
              self.write('Welcome\r')
              self.data = ''  # 用于接收 指令
      
          def data_received(self, data) -> None:
              # print(data)
              if not data:
                  return
              elif data == b'\r\n':
                  message = self.data
                  command = message.strip().split(' ')[0].lower()  # 只是以一个空格符作为分隔符
                  args = message.strip().split(' ')[1:]
                  # print(command)
                  if not hasattr(self, 'command_%s'%command):
                      self.write('\rInvalid command:%s \n\r'%command)
                      self.data = ''
                      return
      
                  try:
                      self.data = ''
                      return getattr(self, 'command_%s'%command)(*args)
                  except Exception as ex:
                      self.write('Error: %s\r'%str(ex))
                  self.data = ''
              else:
                  self.data += data.decode('ascii')
      
          def write(self, msg_string):
              msg_string += '\n'
              self.transport.write(msg_string.encode('ascii', 'ingore'))
      
          def command_add(self, *args):
              # print(args)
              args = [int(i) for i in args]
              self.write('%d\r'%sum(args))
      
          def command_shutdown(self):
              raise KeyboardInterrupt
      
      if __name__ == '__main__':
          loop = asyncio.get_event_loop()
          coro = loop.create_server(ServerProtocol, '127.0.0.1', 8000)
          loop.create_task(coro)
          try:
              loop.run_forever()
          except KeyboardInterrupt:
              pass
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

壹如年少遲夏歸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值