【问题标题】:Iterating a loop using await or yield causes error使用 await 或 yield 迭代循环会导致错误
【发布时间】:2017-01-09 18:47:14
【问题描述】:

我来自Twisted/Klein。我平静地来寻求Tornado的帮助。我正在调查 Tornado 以及它对异步的看法与 Twisted 有何不同。 Twisted 有类似于gen.coroutine 的东西,即defer.inlineCallbacks,我可以编写这样的异步代码:

kleinsample.py

@app.route('/endpoint/<int:n>')
@defer.inlineCallbacks
def myRoute(request, n):
    jsonlist = []
    for i in range(n):
        yield jsonlist.append({'id': i})
    return json.dumps(jsonlist)

卷曲命令:

curl localhost:9000/json/2000

此端点将创建一个包含 n 元素数的 JSON 字符串。 n 可以很小也可以很大。我可以在 Twisted 中分解它,这样事件循环就不会使用 yield 阻塞。现在这是我尝试将其转换为 Tornado 的方法:

tornadosample.py

async def get(self, n):
    jsonlist = []
    for i in range(n):
        await gen.Task(jsonlist.append, {'id': i})    # exception here
    self.write(json.dumps(jsonlist))

回溯:

 TypeError: append() takes no keyword arguments

我对如何正确迭代循环中的每个元素以使事件循环不被阻塞感到困惑。有谁知道这样做的“龙卷风”方式?

【问题讨论】:

    标签: python python-3.x asynchronous tornado coroutine


    【解决方案1】:

    我们来看看gen.Taskdocs

    改编一个基于回调的异步函数以在协程中使用。

    获取一个函数(和可选的附加参数)并使用这些参数加上一个回调关键字参数运行它。传递给回调的参数作为 yield 表达式的结果返回。

    由于append 不接受关键字参数,它不知道如何处理callback kwarg 并吐出该异常。

    您可以做的是用您自己的函数包装 append,该函数接受 callback kwarg 或 this 答案中显示的方法。

    【讨论】:

      【解决方案2】:

      你不能也不能等待append,因为它不是协程也不会返回 Future。如果您想偶尔让步以允许其他协程继续使用 Tornado 的事件循环,请等待 gen.moment

      from tornado import gen
      
      async def get(self, n):
          jsonlist = []
          for i in range(n):
              jsonlist.append({'id': i})
              if not i % 1000:  # Yield control for a moment every 1k ops
                 await gen.moment
          return json.dumps(jsonlist)
      

      也就是说,除非此函数极度占用大量 CPU 资源并且需要数百毫秒或更长时间才能完成,否则您最好一次完成所有计算,而不是多次执行函数返回之前的事件循环。

      【讨论】:

      • 我计划做一些类似于你的答案的事情(比如await Future() 然后做附加)。希望找到更好的解决方案,如果不是你的答案将不得不这样做。它不是 CPU 密集型的,但实际代码(比我帖子中的示例复杂得多)是一个足以破坏我当前同步站点中的其他功能的部分。因此,我希望暂停迭代片刻片刻(所有双关语都是有意的)。
      • 什么会构成比我发布的“更好的解决方案”?我有什么可以帮你的?
      • 抱歉并不是说您的回答不好。我可以发誓上次使用 Tornado 时有一个函数允许我传递一个可迭代或生成器,然后异步触发每个元素的回调。 Twisted 有一个叫做 Cooperator 的东西,它可以做类似的事情,所以我认为 Tornado 也有类似的解决方案。您的解决方案应该可以满足我的要求
      • 啊哈哈!我知道你是谁A. Jesse Jiryu Davis! :D 我最近在 PyCon 上看到了你的视频。
      【解决方案3】:

      list.append() 返回None,因此您的 Klein 样本看起来像是在产生一些对象有点误导。这相当于 jsonlist.append(...); yield 作为两个单独的语句。龙卷风等价物是用await gen.moment 代替裸露的yield

      还要注意,在 Tornado 中,处理程序通过调用 self.write() 而不是返回值来产生响应,因此 return 语句应该是 self.write(json.dumps(jsonlist))

      【讨论】:

      • 是的,我明白它为什么会产生误导。这不是世界上最好的 sn-p,但它让我明白了(至少我认为)。我忘了将return 更改为self.write,因为我是即时编写示例的。谢谢你的收获
      猜你喜欢
      • 1970-01-01
      • 2020-05-30
      • 1970-01-01
      • 2017-07-18
      • 2019-03-19
      • 2017-05-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多