【问题标题】:asyncio awaitable object - basic example异步等待对象 - 基本示例
【发布时间】:2017-12-02 07:08:59
【问题描述】:

我正在尝试了解如何制作一个可等待的对象。 documentation 的定义声明:

具有 __await__ 方法的对象返回一个迭代器。

在该定义的指导下,我编写了示例代码:

import asyncio

async def produce_list():
        num = await Customer()
        print(num)

class Customer(object):

    def __await__(self):
        return iter([1, 2, 3, 4])

loop = asyncio.get_event_loop()
loop.run_until_complete(produce_list())

我预期的流程是:

  1. 事件循环将控制权交给produce_list()produce_list() 放弃对 num = await Customer() 的执行。
  2. Customer() 被执行并返回一个迭代器。这是因为返回迭代器中的第一个值。 Q1: 不清楚为什么 num 本身没有成为迭代器。还有send 在这里做什么?
  3. 一旦达到迭代器的最后一个值。 num = 4 协程的执行继续到print(num),并打印值 4。

我得到了什么:

---------------------------------------------------------------------------
RuntimeError                              Traceback (most recent call last)
~/workspace/dashboard/so_question_await.py in <module>()
     16 
     17 loop = asyncio.get_event_loop()
---> 18 loop.run_until_complete(produce_list())

/usr/lib/python3.5/asyncio/base_events.py in run_until_complete(self, future)
    464             raise RuntimeError('Event loop stopped before Future completed.')
    465 
--> 466         return future.result()
    467 
    468     def stop(self):

/usr/lib/python3.5/asyncio/futures.py in result(self)
    291             self._tb_logger = None
    292         if self._exception is not None:
--> 293             raise self._exception
    294         return self._result
    295 

/usr/lib/python3.5/asyncio/tasks.py in _step(***failed resolving arguments***)
    239                 result = coro.send(None)
    240             else:
--> 241                 result = coro.throw(exc)
    242         except StopIteration as exc:
    243             self.set_result(exc.value)

~/workspace/dashboard/so_question_await.py in produce_list()
      5 
      6 async def produce_list():
----> 7         num = await Customer()
      8         print(num)
      9 

RuntimeError: Task got bad yield: 1

我在这里弄错了哪些概念?

最后我在找一个例子,它使用通过列表的迭代作为事件返回到协程的控制。

【问题讨论】:

    标签: python python-3.x asynchronous async-await python-asyncio


    【解决方案1】:

    __await__ 返回一个迭代器,因为协程的底层机制最初是基于yield from 语法。在实践中,__await__ 返回iter(some_future)some_coroutine.__await__()。它可用于创建每次等待时产生不同值的对象。看这个简单的例子:

    import asyncio
    import random
    
    class RandomProducer:
    
        def __await__(self):
            return self.producer().__await__()
    
        async def producer(self):
            sleep = random.random()
            value = random.randint(0, 9)
            return await asyncio.sleep(sleep, result=value)
    
    async def main():
        producer = RandomProducer()
        while True:
            print(await producer)
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    

    回答你的cmets:

    每个协程最终都会调用asyncio.sleep吗?

    不,asyncio.sleep 实际上不是链的末端。在最底层,它始终是一个正在产生的未来:协程链要求事件循环“当这个未来有结果时请叫醒我”。在asyncio.sleep 的情况下,它使用loop.call_later 在给定时间后设置未来的结果。循环提供了更多调度回调的方法:loop.call_atloop.add_readerloop.add_writerloop.add_signal_handler等。

    一个 asyncio 库,例如 aiohttp。我假设某处有一些代码不依赖于以前的协同程序的存在。

    为了实现单线程并发,所有的 IO 操作最终都必须委托给事件循环。 例如,aiohttp 依赖于 loop.create_connection 协程到 manage the TCP connection

    【讨论】:

    • 我实际上是在尝试获取一个最终不调用先前定义的 asyncio lib 方法的示例。最后你通过了await asyncio.sleep(sleep, result=value)。动机是尝试了解如何编写新的 asyncio 框架。
    • 或者换个说法。每个协程最终都会调用asyncio.sleep吗?
    • @TheMeaningfulEngineer how would one approach the writing of a new asyncio framework. 你是指像aiohttp 这样的异步库还是像curio 这样的异步替代库?另外,请参阅我的编辑。
    • 一个 asyncio 库,例如 aiohttp。我假设某处有一些代码不依赖于以前的协同程序的存在(这是我试图通过Customer 示例实现的。
    • 看起来coroutine model 主要基于event loop。由于 CPU 密集型实际上不需要这种用户空间线程。 coroutine 仅在遇到 I/O 任务时才有价值。那么我们能不能把await async_client_request()当成一个进程:1.将TCP socketwritable事件注册到event loop,2.当它可写时,再将http client写内容到socket,然后注册read事件,3.当它可读时,然后future返回,代码再次从await行开始
    【解决方案2】:

    @Vincent 的答案可能是你想要的,因为它会让你使用你的__await__ 方法来调用更多的异步代码。

    但是,a bit of searching through python's implementation of Tasks 表明 async 返回的迭代器应该要么返回 None(将控制发送回事件循环),要么引发异常,其值为它们的返回值。 p>

    因此,以下工作:

    class MyIter:
        def __init__(self):
            self.n = 2
        def __next__(self):
            self.n -= 1
            if self.n >= 0:
                return None # yields control to outer loop
            raise StopIteration(12) # "return" value stored in exc.value
    
    class MyTask:
        def __await__(self):
            return MyIter()
    
    async def test():
        ans = await MyTask()
        print(ans)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-16
      • 2018-01-05
      • 2020-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-18
      相关资源
      最近更新 更多