• asyncio异步模块的21个协程编写实例


    1. 启动一个无返回值协程

      通过async关键字定义一个协程

      import sys
      import asyncio
      
      async def coroutine():
          print('运行协程')
          
      if sys.version_info >= (3, 7, 0):
          asyncio.run(coroutine())
      else:
          loop = asyncio.get_event_loop()
          loop.run_until_complete(coroutine())
          loop.close()
      

      输出结果如下

      运行协程
      
    2. 启动一个有返回值协程

      import sys
      import asyncio
      
      async def coroutine():
          print('运行协程')
          return 'done!'
        
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(coroutine())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(coroutine())
          loop.close()
      print(result)
      

      输出结果如下

      运行协程
      done!
      
    3. 使用await关键字

      await关键字只能在一个协程内部使用

      await等待另一个协程任务结果,它不会阻塞事件循环,但会在阻塞当前协程的上下文

      import sys
      import asyncio
      
      async def coroutine():
          print('运行协程 调度asyncio.sleep协程任务')
          await asyncio.sleep(2)
          return 'done!'
        
      print('启动时间:', time.time())
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(coroutine())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(coroutine())
          loop.close()
      print(result)
      print('结束时间:', time.time())
      

      输出结果如下

      启动时间: 1598236154.90588
      运行协程 调度asyncio.sleep协程任务
      done!
      结束时间: 1598236156.9118028
      
    4. 回调函数: 迅速回调

      迅速回调是立即主动调用一个方法

      回调方法必然是一个普通函数

      回调通过事件循环loop提供的方法添加,它不会阻塞协程上下文

      同一个协程内的回调函数之间存在阻塞关系,不同协程内的回调函数之间无阻塞关系

      import sys
      import asyncio
      
      def callback(a, c=2):
          time.sleep(c)
          print(f"传入参数a={a}, c={c}, 时间{time.time()}")
      
      async def main():
          print(f'添加回调, 时间{time.time()}')
          loop = asyncio.get_running_loop()
          loop.call_soon(callback, 1)
          loop.call_soon(callback, 2, 0)
          print(f'协程结束, 时间{time.time()}')
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      添加回调, 时间1598236958.119624
      协程结束, 时间1598236958.1196742
      传入参数a=1, c=2, 时间1598236960.1198878
      传入参数a=2, c=0, 时间1598236960.119946
      
    5. 回调函数:延时回调

      延时回调可以指定在多久后执行回调函数

      延时回调的其他特性和迅速回调一致

      特别注意,如果事件循环内的协程已经运行结束,尚在等待调用的回调函数不会被调用

      import sys
      import asyncio
      
      def callback(a, c=3):
          time.sleep(c)
          print(f"传入参数a={a}, c={c}, 时间{time.time()}")
      
      async def main():
          print(f'添加回调, 时间{time.time()}')
          loop = asyncio.get_running_loop()
          loop.call_later(1, callback, 2, 0)
          loop.call_soon(callback, 1)
      
          print(f'协程结束, 时间{time.time()}')
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      添加回调, 时间1598237313.375363
      协程结束, 时间1598237313.375415
      传入参数a=1, c=3, 时间1598237316.376031
      传入参数a=2, c=0, 时间1598237316.376389
      

      解释说明,对于协程main,call_later和call_soon是不会阻塞协程上下文的,因此两个回调函数可以视为同时加入了事件循环的回调,其中回调callback(1)方法是立即调用,回调函数会sleep 3秒,而回调方法callback(2, 0)是在协程延时 1秒后调用,但回调函数之间是存在阻塞关系的,因此它会等待callback(1)先运行结束,然后判断是否满足了延时条件再执行。

    6. 回调函数:定时回调

      此处的指定时间并非系统时间戳,而是指事件循环创建的时间戳

      通过loop.time()获取事件循环时间戳

      import sys
      import asyncio
      
      def callback(a, loop, c=3):
          time.sleep(c)
          print(f"传入参数a={a}, c={c}, 时间{loop.time()}")
      
      
      async def main():
          loop = asyncio.get_running_loop()
          now = loop.time()
          print(f'事件循环时间戳{now}')
          loop.call_at(now + 4, callback, 2, loop, 1)
          loop.call_soon(callback, 1, loop)
          await asyncio.sleep(5)
          print(f'协程结束, 时间戳{loop.time()}')
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      事件循环时间戳0.143745276
      传入参数a=1, c=3, 时间3.145988962
      传入参数a=2, c=1, 时间5.147769435
      协程结束, 时间戳5.147995384
      
    7. asyncio.Future对象

      Future的实例化对象可以认为是一个协程对象,它可以使用await关键字

      通过延时回调+Future可以模拟一个协程的逻辑

      即延时回调 ≈ 异步等待返回结果

      Future ≈ 非阻塞模型,它不会阻塞事件循环的其他协程

      import sys
      import asyncio
      
      def mark_done(future: asyncio.Future, result):
          print(f'标记future结束  时间{time.time()}')
          future.set_result(result)
      
      
      async def main():
          print(f'协程开始  时间{time.time()}')
          loop = asyncio.get_running_loop()
          future = asyncio.Future()
          loop.call_later(3, mark_done, future, 'done!')
          result = await future
          print(f'future结果 {result}  时间{time.time()}')
      
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      协程开始  时间1598238858.6768472
      标记future结束  时间1598238861.678293
      future结果 done!  时间1598238861.678438
      
    8. Future回调

      Future的回调是在一个Future对象标记结束后运行的函数

      await会等待回调函数执行结束,即回调函数会在协程上下文阻塞

      import sys
      import asyncio
      
      def callback(future: asyncio.Future):
          # 基于Future的回调函数一定要接受一个Future对象
          print(f'Future回调被调用 时间{time.time()}')
          time.sleep(1)
      
      
      async def main():
          print(f'协程开始  时间{time.time()}')
          loop = asyncio.get_running_loop()
          future = asyncio.Future()
          future.add_done_callback(callback)
          loop.call_later(1, lambda future: future.set_result('done'), future)
          result = await future
          print(f'future结果 {result} 时间{time.time()}')
      
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      协程开始  时间1598241100.534262
      Future回调被调用 时间1598241101.539032
      future结果 done 时间1598241102.5411909
      
    9. aysncio.Task对象

      Task对象在调用create_task会立即执行,它类似迅速回调

      asyncio.create_task是python3.7引入的高级api,3.7以下使用ensure_future方法

      与回调函数的区别:

      • 回调函数一定不是协程,而Task对象只能创建协程任务
      • 回调函数的传入参数直接通过添加回调的方法传入,而任务对象直接传入协程形参
      • 回调函数之间运行是阻塞的,而Task对象则是基于事件循环的标准协程

      在协程上下文中可以使用await关键字等待任务结果

      import sys
      import asyncio
      
      async def task_func(n):
          print(f'运行 task_func  时间{time.time()}')
          await asyncio.sleep(n)
          return 'task done!'
      
      
      async def main():
          loop = asyncio.get_running_loop()
          task = loop.create_task(task_func(3))
          result = await task
          print(f'task结果 {result} 时间{time.time()}')
      
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      运行 task_func  时间1598241581.334356
      task结果 task done! 时间1598241584.338337
      
    10. Task取消任务

      可以取消一个正在事件循环内运行的task对象

      Task和Future一样,支持通过add_done_callback添加回调函数

      import sys
      import asyncio
      
      async def task_func(n):
          print(f'运行 task_func  时间{time.time()}')
          await asyncio.sleep(n)
          return 'task done!'
      
      
      async def main():
          loop = asyncio.get_running_loop()
          task = asyncio.create_task(task_func(5))
          loop.call_later(3, lambda task: task.cancel(), task)
          # Task被取消 会抛出CancelledError异常
          try:
              result = await task
              print(f'task结果 {result} 时间{time.time()}')
          except asyncio.exceptions.CancelledError:
              print(f'task被取消了 时间{time.time()}')
      
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      运行 task_func  时间1598242256.3299708
      task被取消了 时间1598242259.335436
      
    11. 使用asyncio.ensure_future创建任务

      asyncio.ensure_future实现效果和asyncio.create_task一致

      import sys
      import asyncio
      
      async def task_func(n):
          print(f'运行 task_func  时间{time.time()}')
          await asyncio.sleep(n)
          return 'task done!'
      
      
      async def main():
          print(f'协程开始  时间{time.time()}')
          task = asyncio.ensure_future(task_func(2))
          result = await task
          print(f'task结果 {result} 时间{time.time()}')
      
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      协程开始  时间1598249260.1346428
      运行 task_func  时间1598249260.1347158
      task结果 task done! 时间1598249262.137878
      
    12. asyncio.wait执行多个协程

      asyncio.wait接受一个协程列表/元组将其加入事件循环

      返回两个列表,分别包含已完成和正在执行的Future对象

      asyncio.wait 会阻塞协程上下文直至满足指定的条件(默认条件所有协程运行结束)

      wait支持设置一个超时时间,但在超时发生时不会取消可等待对象,但若事件循环结束时未完成则会抛出CancelledError异常。如果要超时主动取消,可用wait_for方法

      asyncio.wait 返回的结果集是按照事件循环中的任务完成顺序排列的,所以通常和原始任务顺序不同

      import sys
      import asyncio
      
      async def corn_sleep(n):
          try:
              await asyncio.sleep(n)
          except asyncio.exceptions.CancelledError:
              print(f'corn({n})超时取消!  时间{time.time()}')
          print(f'corn({n}) done!  时间{time.time()}')
          return f'corn({n}) done!  时间{time.time()}'
      
      
      async def main():
          print(f'协程开始  时间{time.time()}')
          tasks = [corn_sleep(i) for i in range(1, 6)]
          done, pending = await asyncio.wait(tasks, timeout=3)
          for task in done:
              pass
              # print(task.result())
          for task in pending:
              print(task.done())
          await asyncio.sleep(1)
          print(f'协程结束  时间{time.time()}')
      
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      协程开始  时间1598253388.614105
      corn(1) done!  时间1598253389.6196852
      corn(2) done!  时间1598253390.6171541
      False
      False
      False
      corn(3) done!  时间1598253391.617078
      corn(4) done!  时间1598253392.617147
      协程结束  时间1598253392.617195
      corn(5)超时取消!  时间1598253392.6173718
      corn(5) done!  时间1598253392.6173818
      
    13. asyncio.gather执行多个协程

      gather方法和wait都可以执行多个协程,但输入和输出有所差异

      • 输出差异,gather保证了结果列表的顺序,它是严格遵循传入任务顺序的
      • 输入差异,wait方法接受的是一个协程列表,而gather是通过可变长参数传入协程方法的
      • wait支持超时设置,gather无法设置超时时间
      • wait返回两个列表,列表元素是Future对象,gather只返回done列表,列表元素是Future对象的result()结果
      import sys
      import asyncio
      
      async def corn_sleep(n):
          await asyncio.sleep(n)
          return f'corn({n}) done!  时间{time.time()}'
      
      
      async def main():
          print(f'协程开始  时间{time.time()}')
          tasks = [corn_sleep(i) for i in range(1, 6)]
          done = await asyncio.gather(*tasks)
          for result in done:
              print(result)
          print(f'协程结束  时间{time.time()}')
      
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      协程开始  时间1598250547.5619462
      corn(1) done!  时间1598250548.567125
      corn(2) done!  时间1598250549.5660028
      corn(3) done!  时间1598250550.563731
      corn(4) done!  时间1598250551.566098
      corn(5) done!  时间1598250552.566897
      协程结束  时间1598250552.566983
      
    14. asyncio.as_completed执行多个协程

      as_completed方法功能同wait和gather都可以用于执行多个协程

      as_completed接受的是协程列表,返回的是一个迭代器,迭代元素为Future对象

      as_completed方法支持超时设置,但它会在协程上下文抛出asyncio.exceptions.TimeoutError错误

      as_completed方法返回结果集是无序的

      import sys
      import asyncio
      
      async def corn_sleep(n):
          await asyncio.sleep(n)
          return f'corn({n}) done!  时间{time.time()}'
      
      
      async def main():
          print(f'协程开始  时间{time.time()}')
          tasks = [corn_sleep(i) for i in range(1, 6)]
          for task in asyncio.as_completed(tasks, timeout=3.5):
              try:
                  result = await task
                  print(result)
              except asyncio.exceptions.TimeoutError:
                  print(f'协程超时了  时间{time.time()}')
                  break
          print(f'协程结束  时间{time.time()}')
      
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      协程开始  时间1598251179.489662
      corn(1) done!  时间1598251180.493866
      corn(2) done!  时间1598251181.4911182
      corn(3) done!  时间1598251182.493823
      协程超时了  时间1598251182.991565
      协程结束  时间1598251182.991592
      
    15. 协程锁

      相比线程、进程锁,协程锁似乎应用场景不那么多,比如保存文件时?

      import sys
      import asyncio
      from functools import partial
      
      def callback(lock: asyncio.Lock):
          print(f'释放锁  时间{time.time()}')
          lock.release()
      
      
      async def corn1(x, loop, lock):
          async with lock:
              pass
          # 加锁,利用延迟回调1秒后解锁
          await lock.acquire()
          loop.call_later(1, callback, lock)
          return f'{x} done! 时间{time.time()}'
      
      
      async def main():
          loop = asyncio.get_running_loop()
          lock = asyncio.Lock()
          await lock.acquire()
          print(f'加锁 时间{time.time()}')
          loop.call_later(2, callback, lock)
          corn = partial(corn1, loop=loop, lock=lock)
          results = await asyncio.gather(corn(1), corn(2), corn(3))
          for result in results:
              print(result)
      
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      加锁 时间1598251855.600318
      释放锁  时间1598251857.6041799
      释放锁  时间1598251858.606617
      释放锁  时间1598251859.609798
      1 done! 时间1598251857.604349
      2 done! 时间1598251858.606834
      3 done! 时间1598251859.6099358
      
    16. Event 事件对象

      Event对象和Lock对象有一定的相似性,可用于同步操作

      Event对象提供一个状态标记位,它是一个布尔值,只用来判断状态

      import sys
      import asyncio
      
      def task_done(event: asyncio.Event):
          event.set()
      
      
      async def corn(event: asyncio.Event):
          await event.wait()
          print(f'corn done!  时间{time.time()}')
      
      
      async def main():
          print(f'协程开始  时间{time.time()}')
          event = asyncio.Event()
          loop = asyncio.get_running_loop()
          loop.call_later(2, task_done, event)
          await corn(event)
      
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      协程开始  时间1598252539.2691798
      corn done!  时间1598252541.273142
      
    17. Queue 协程队列

      与普通队列相比,协程队列有一个task_done方法,用于标记某一个任务结束,而join方法则会阻塞,直到满足从队列取出的个数等于task_done方法调用的次数

      import sys
      import asyncio
      
      async def producer(queue: asyncio.Queue):
          print('生产者 上班')
          for i in range(1, 6):
              await queue.put(f'产品({i})')
              await asyncio.sleep(2)
          await queue.put(None)
          await queue.join()
          print('生产者 打烊')
      
      
      async def consumer(x, queue: asyncio.Queue):
          print(f'消费者{x}号进场')
          while True:
              pt = await queue.get()
              queue.task_done()
              if pt is None:
                  await queue.put(None)
                  break
              else:
                  print(f'{x}号 消费了 {pt}')
      
      
      async def main():
          loop = asyncio.get_running_loop()
          queue = asyncio.Queue(maxsize=3)
          loop.create_task(producer(queue))
          await asyncio.wait([consumer(i, queue) for i in range(3)])
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      输出结果如下

      生产者 上班
      消费者2号进场
      2号 消费了 产品(1)
      消费者0号进场
      消费者1号进场
      2号 消费了 产品(2)
      0号 消费了 产品(3)
      1号 消费了 产品(4)
      2号 消费了 产品(5)
      生产者 下班
      
    18. asyncio.subprocess 异步调用子进程

      asyncio提供create_subprocess_exec和create_subprocess_shell方法

      前者可以调用任意程序,后者则通过shell命令行调用其他程序

      import sys
      import asyncio
      
      async def get_date():
          code = 'import datetime; print(datetime.datetime.now())'
          task1 = asyncio.create_subprocess_exec(
              sys.executable, '-c', code,
              stdout=asyncio.subprocess.PIPE
          )
          task2 = asyncio.create_subprocess_shell(
              'ls -all ~/Desktop', stdout=asyncio.subprocess.PIPE
          )
          results = await asyncio.gather(task1, task2)
          response = []
          for proc in results:
              data = await proc.stdout.read()
              line = data.decode('utf8').rstrip()
              response.append(line)
          return '
      '.join(response)
      
      date = asyncio.run(get_date())
      print(f"当前时间: {date}")
      

      输出结果如下

      当前时间: 2020-08-24 16:11:18.143943
      total 16
      drwxr-xr-x   3 sw  staff    96 Apr  6 09:11 $RECYCLE.BIN
      drwx------@  8 sw  staff   256 Jun 18 10:52 .
      drwxr-xr-x+ 63 sw  staff  2016 Aug 23 20:52 ..
      -rw-r--r--@  1 sw  staff  6148 Jul 22 17:54 .DS_Store
      -rw-r--r--   1 sw  staff     0 May 27  2019 .localized
      drwxr-xr-x   3 sw  staff    96 Feb  3  2020 Don't Starve Together.app
      drwxr-xr-x   9 sw  staff   288 May  3 18:24 HeavenMS
      drwxr-xr-x   3 sw  staff    96 Sep 20  2019 Tomb Raider.app
      
    19. 网络通信-高级API

      asyncio封装了几个高级方法来快速实现网络通信

      • await asyncio.open_connection() 建立TCP连接
      • await asyncio.open_unix_connection() 建立Unix socket连接
      • await start_server() 启动TCP服务
      • await start_unix_server() 启动Unix socket服务
      • StreamReader 接收网络数据的高级async/await对象
      • StreamWriter 发送网络数据的高级async/await对象

      以搭建一个简单http服务器为例

      import sys
      import asyncio
      
      async def http_handle(render: asyncio.StreamReader,
                            writer: asyncio.WriteTransport):
          """在asyncio.start_server传入该协程
          当一个tcp连接建立时就会调用该回调,并传入两个参数
          :param render: StreamReader对象
          :param writer: StreamWriter对象
          StreamReader和StreamWriter对象都是继承于Transport
          :return:
          """
          message = await render.read(1024)
          print(message.decode())
          writer.write(b'HTTP/1.1 200 OK
      
      Hello Client!')
          writer.close()
      
      
      async def main():
          server = await asyncio.start_server(http_handle, '127.0.0.1', 8000)
          addr = server.sockets[0].getsockname()
          print(f'服务器启动 {addr}')
          async with server:
              await server.serve_forever()
      
      
      if sys.version_info >= (3, 7, 0):
          result = asyncio.run(main())
      else:
          loop = asyncio.get_event_loop()
          result = loop.run_until_complete(main())
          loop.close()
      

      浏览器打开http://127.0.0.1:8000 控制台输出结果如下

      GET / HTTP/1.1
      Host: 127.0.0.1:8000
      Connection: keep-alive
      Cache-Control: max-age=0
      Upgrade-Insecure-Requests: 1
      User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.135 Safari/537.36
      Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
      Sec-Fetch-Site: none
      Sec-Fetch-Mode: navigate
      Sec-Fetch-User: ?1
      Sec-Fetch-Dest: document
      Accept-Encoding: gzip, deflate, br
      Accept-Language: zh-CN,zh;q=0.9
      

      浏览器显示结果

      Hello Client!
      
    20. 网络通信-低级API

      案例19所用的方法是基于底层Transport和Protocol封装的结果

      Protocol协议类是通过重写一系列回调方法来处理网络请求

      Transport实例可以简单看作是对一个socket对象封装,实现了数据收发功能

      除了网络通信外,子进程(进程通信)也是通过协议回调来实现

      以一个tcp服务端为例,较常用的有4个回调函数

      • connection_made() 连接建立时被调用
      • connection_lost() 连接丢失或关闭时被调用
      • data_received() 接收到数据时被调用
      • eof_received() 接收到EOF时被调用

      以案例19的http服务端为例,实现相同效果

      import sys
      import asyncio
      
      class HttpProtocol(asyncio.Protocol):
          def __init__(self):
              """每个tcp连接建立时都会实例化这个类"""
              self.transport: asyncio.Transport = None
      
          def connection_made(self, transport: asyncio.Transport):
              """连接建立时被调用"""
              self.transport = transport
      
          def data_received(self, data):
              """接收数据时被调用"""
              print(data.decode())
              self.transport.write(b'HTTP/1.1 200 OK
      
      Hello Client!')
              self.transport.close()
      
      
      loop = asyncio.get_event_loop()
      server = loop.create_server(HttpProtocol, '127.0.0.1', 8000)
      loop.run_until_complete(server)
      loop.run_forever()
      
    21. 信号处理

      事件循环能够支持添加信号来执行回调函数

      import signal
      import asyncio
      
      def ask_exit(sig_name: str, loop: asyncio.AbstractEventLoop):
          print("捕获信号 %s: exit" % sig_name)
          loop.stop()
      
      
      async def main():
          loop = asyncio.get_running_loop()
          for signame in {'SIGINT', 'SIGTERM'}:
              loop.add_signal_handler(
                  getattr(signal, signame),
                  partial(ask_exit, signame, loop))
      
          await asyncio.sleep(3600)
      
      print("事件循环将在1小时后或者按下Ctrl+C停止")
      try:
          asyncio.run(main())
      except RuntimeError as e:
          if str(e) != 'Event loop stopped before Future completed.':
              raise
      

      输出结果如下

      事件循环将在1小时后或者按下Ctrl+C停止
      ^C捕获信号 SIGINT: exit
      

    高频方法总结

    1. 回调和Task具有一定相似性,回调是函数,任务是协程

    2. 向事件循环非阻塞运行一个协程是创建一个任务,可以使用如下方法

      方法 传入 返回 备注
      asyncio.create_task 协程 Task对象 高层级api、Python3.7 +
      asyncio.ensure_future 协程 Task对象 高层级api
      loop.create_task 协程 Task对象 低层级api
    3. 向事件循环添加多个协程(使用await关键词才会实际添加运行),可使用如下方法

      方法 传入 返回 备注
      asyncio.wait 协程列表 done/pending列表
      元素是Future对象
      支持超时设置,结果无序
      asyncio.gather 变长参数 协程返回值组成的列表 返回结果列表有序
      asyncio.as_completed 协程列表 协程任务迭代器 通过for循环遍历获取任务结果
    4. 在unix平台,可以使用性能更好uvloop来替代asyncio默认的事件循环

      import uvloop
      asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
      

  • 相关阅读:
    福大软工1816 · 第四次作业
    福大软工1816 · 第三次作业
    福大软工1816 · 第二次作业
    福大软工1816 · 第四次作业
    福大软工1816 · 第三次作业
    Alpha 冲刺 (4/10)
    Alpha 冲刺 (3/10)
    Alpha 冲刺 (2/10)
    Alpha 冲刺 (1/10)
    项目需求分析
  • 原文地址:https://www.cnblogs.com/lazyfish007/p/13559026.html
Copyright © 2020-2023  润新知