探索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()且故障是一样的.
事实证明,问题在于事件循环的上下文.
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())