使用asyncio.coroutine方法在类外部声明的asyncio event_loop失败,并出现"AttributeError:'NoneType'对象没有属性'select'"

shi*_*die 4 python class

探索Python 3.4.0的asyncio模块,我试图创建一个带有asyncio.coroutine方法的类,这些方法是从类外部的event_loop调用的.

我的工作代码如下.

import asyncio

class Foo():
    @asyncio.coroutine
    def async_sleep(self):
        print('about to async sleep')
        yield from asyncio.sleep(1)

    @asyncio.coroutine
    def call_as(self):
        print('about to call ass')
        yield from self.async_sleep()

    def run_loop(self):
        loop = asyncio.get_event_loop()
        loop.run_until_complete(self.call_as())
        print('done with loop')
        loop.close()

a = Foo()
a.run_loop()

loop = asyncio.get_event_loop()
loop.run_until_complete(a.call_as())
Run Code Online (Sandbox Code Playgroud)

调用a.run_loop()按预期提供输出:

python3 async_class.py
about to call ass
about to async sleep
done with loop
Run Code Online (Sandbox Code Playgroud)

但是,只要event_loop尝试处理,a.call_as()我就会获得以下Traceback:

Traceback (most recent call last):
  File "async_class.py", line 26, in <module>
    doop.run_until_complete(asyncio.async(a.call_ass()))
  File "/opt/boxen/homebrew/opt/pyenv/versions/3.4.0/lib/python3.4/asyncio/base_events.py", line 203, in run_until_complete
self.run_forever()
  File "/opt/boxen/homebrew/opt/pyenv/versions/3.4.0/lib/python3.4/asyncio/base_events.py", line 184, in run_forever
self._run_once()
  File "/opt/boxen/homebrew/opt/pyenv/versions/3.4.0/lib/python3.4/asyncio/base_events.py", line 778, in _run_once
event_list = self._selector.select(timeout)
AttributeError: 'NoneType' object has no attribute 'select'
Run Code Online (Sandbox Code Playgroud)

我试图包裹a.call_as()asyncio.Task(),asyncio.async()且故障是一样的.

shi*_*die 5

事实证明,问题在于事件循环的上下文.

asyncio在运行时神奇地为线程创建一个事件循环..get_event_loop()调用此事件循环的上下文时设置.

在上面的示例中,a.run_loop将事件循环设置在上下文中Foo.run_loop.

事件循环的一个问题是每个线程可能只有一个事件循环,并且给定的事件循环只能处理其上下文中的事件.

考虑到这一点,请注意loop = asyncio.get_event_loop()刚才a.run_loop要求将线程的事件循环分配给__main__上下文.不幸的是,事件循环已经设置为上下文Foo.run_loop,因此None__main__事件循环设置了一个类型.

相反,有必要创建一个新的事件循环,然后将该事件循环的上下文设置为__main__,即

new_loop = asyncio.new_event_loop()
asyncio.set_event_loop(new_loop)
Run Code Online (Sandbox Code Playgroud)

只有这样才能在上下文中正确设置事件循环__main__,从而允许正确执行我们现在修改的new_loop.run_until_complete(a.call_as())