异步操作应该用@asyncio.coroutine修饰什么?
What should I decorate with @asyncio.coroutine for async operations?
我想运行我的代码异步。我应该用 @asyncio.coroutine
装饰什么?我应该用 yield from
调用什么进行异步操作?
就我而言,我有一些没有装饰器的示例代码。 (简单的聊天机器人看起来像 IRC)
import asyncio
class ChatBot:
def __init__(self, loop):
conn = asyncio.open_connection(HOST, PORT, loop=loop)
self.reader, self.writer = yield from conn
def send(self, msg):
self.writer.write(msg)
def read(self):
msg = yield from self.reader.readline()
return msg
def run(self):
while True:
msg = self.read()
self.parse(msg)
def parse(self, msg):
if msg.startswith('PING'):
self.some_work(msg)
elif msg.startswith('ERROR'):
self.some_error()
else:
self.server_log(msg)
def some_work(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
def some_error(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
def server_log(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
loop = asyncio.get_event_loop()
bot = ChatBot(loop)
loop.run_until_complete(???)
loop.close()
我认为???
是bot.run()
,ChatBot.run
必须用@asyncio.coroutine
修饰。那么,其他方法呢?我无法理解何时使用 @asyncio.coroutine
装饰器并使用 yield from
或 asyncio.async
调用方法。 (我已经阅读了 PEP-3156 以了解 asnycio。但我无法完全理解。)
@asyncio.coroutine
def read(self):
msg = yield from self.reader.readline()
return msg
@asyncio.coroutine
def run(loop):
while True:
msg = yield from read()
yield from parse(msg)
loop = asyncio.get_event_loop()
loop.run_until_complete(run(loop))
loop.close()
你应该装饰所有使用 yield from
的东西,因为装饰器 asyncio.coroutine
会将你的函数作为生成器,并在你产生时完成所有 callback/async 工作。
在您的情况下,run
需要改写如下:
@asyncio.coroutine
def run(self):
while True:
msg = yield from self.read()
yield from self.parse(msg)
那么,read
和parse
也必须是协程。
在使用异步之前,你应该了解它是如何工作的,它会对你有很大帮助。
何时使用 @asyncio.coroutine
装饰器
如果你有一个函数需要使用yield from
调用协程,你应该用asyncio.coroutine
装饰它。另请注意,协程通常(并非总是)"viral"。一旦将 yield from
添加到一个函数,它就会成为一个协程,此外,任何 调用 该协程的函数通常(尽管不总是)也需要成为一个协程.
何时使用asyncio.async
为什么协程并不总是流行?因为你实际上并不总是需要使用 yield from
来调用协程。如果你想调用协程并等待它完成,你只需要使用yield from
。如果你只是想在后台启动一个协程,你可以这样做:
asyncio.async(coroutine())
一旦控制 returns 到事件循环,这将安排 coroutine
到 运行;它不会等到 coroutine
完成后再继续下一行。一个普通的函数可以使用它来安排一个协程到 运行 而不必自己也成为一个协程。
您还可以使用此方法同时 运行 多个 coroutines
。所以,假设您有这两个协程:
@asyncio.coroutine
def coro1():
yield from asyncio.sleep(1)
print("coro1")
@asyncio.coroutine
def coro2():
yield from asyncio.sleep(2)
print("coro2")
如果你有这个:
@asyncio.coroutine
def main():
yield from coro1()
yield from coro2()
yield from asyncio.sleep(5)
asyncio.get_event_loop().run_until_complete(main())
1 秒后,将打印 "coro1"
。然后,再过两秒(总共三秒),将打印 "coro2"
,五秒后程序将退出,共计 8 秒 运行 时间。或者,如果您使用 asyncio.async
:
@asyncio.coroutine
def main():
asyncio.async(coro1())
asyncio.async(coro2())
yield from asyncio.sleep(5)
asyncio.get_event_loop().run_until_complete(main())
这将在一秒后打印 "coro1"
,一秒后打印 "coro2"
,程序将在 3 秒后退出,总共 运行 时间为 5 秒。
这对您的代码有何影响?
因此,按照这些规则,您的代码需要如下所示:
import asyncio
class ChatBot:
def __init__(self, reader, writer):
# __init__ shouldn't be a coroutine, otherwise you won't be able
# to instantiate ChatBot properly. So I've removed the code that
# used yield from, and moved it outside of __init__.
#conn = asyncio.open_connection(HOST, PORT, loop=loop)
#self.reader, self.writer = yield from conn
self.reader, self.writer = reader, writer
def send(self, msg):
# writer.write is not a coroutine, so you
# don't use 'yield from', and send itself doesn't
# need to be a coroutine.
self.writer.write(msg)
@asyncio.coroutine
def read(self):
msg = yield from self.reader.readline()
return msg
@asyncio.coroutine
def run(self):
while True:
msg = yield from self.read()
yield from self.parse(msg)
@asyncio.coroutine
def parse(self, msg):
if msg.startswith('PING'):
yield from self.some_work(msg)
elif msg.startswith('ERROR'):
yield from self.some_error()
else:
yield from self.server_log(msg)
@asyncio.coroutine
def some_work(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
@asyncio.coroutine
def some_error(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
@asyncio.coroutine
def server_log(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send()
@asyncio.coroutine
def main(host, port):
reader, writer = yield from asyncio.open_connection(HOST, PORT, loop=loop)
bot = ChatBot(reader, writer)
yield from bot.run()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
还有一件事要记住——在函数前添加 yield from
并不能神奇地使该调用成为非阻塞的。添加 @asyncio.coroutine
装饰器也没有。只有当函数实际上直接或间接调用本机 asyncio
协程时,它们才是非阻塞的,这些协程使用非阻塞 I/O 并与 asyncio
事件循环集成。例如,您提到进行 REST API 调用。为了使那些 REST API 调用不阻塞事件循环,您需要使用 aiohttp
库或 asyncio.open_connection
。使用 requests
或 urllib
之类的东西会阻塞循环,因为它们没有与 `asyncio.
集成
我想运行我的代码异步。我应该用 @asyncio.coroutine
装饰什么?我应该用 yield from
调用什么进行异步操作?
就我而言,我有一些没有装饰器的示例代码。 (简单的聊天机器人看起来像 IRC)
import asyncio
class ChatBot:
def __init__(self, loop):
conn = asyncio.open_connection(HOST, PORT, loop=loop)
self.reader, self.writer = yield from conn
def send(self, msg):
self.writer.write(msg)
def read(self):
msg = yield from self.reader.readline()
return msg
def run(self):
while True:
msg = self.read()
self.parse(msg)
def parse(self, msg):
if msg.startswith('PING'):
self.some_work(msg)
elif msg.startswith('ERROR'):
self.some_error()
else:
self.server_log(msg)
def some_work(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
def some_error(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
def server_log(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
loop = asyncio.get_event_loop()
bot = ChatBot(loop)
loop.run_until_complete(???)
loop.close()
我认为???
是bot.run()
,ChatBot.run
必须用@asyncio.coroutine
修饰。那么,其他方法呢?我无法理解何时使用 @asyncio.coroutine
装饰器并使用 yield from
或 asyncio.async
调用方法。 (我已经阅读了 PEP-3156 以了解 asnycio。但我无法完全理解。)
@asyncio.coroutine
def read(self):
msg = yield from self.reader.readline()
return msg
@asyncio.coroutine
def run(loop):
while True:
msg = yield from read()
yield from parse(msg)
loop = asyncio.get_event_loop()
loop.run_until_complete(run(loop))
loop.close()
你应该装饰所有使用 yield from
的东西,因为装饰器 asyncio.coroutine
会将你的函数作为生成器,并在你产生时完成所有 callback/async 工作。
在您的情况下,run
需要改写如下:
@asyncio.coroutine
def run(self):
while True:
msg = yield from self.read()
yield from self.parse(msg)
那么,read
和parse
也必须是协程。
在使用异步之前,你应该了解它是如何工作的,它会对你有很大帮助。
何时使用 @asyncio.coroutine
装饰器
如果你有一个函数需要使用yield from
调用协程,你应该用asyncio.coroutine
装饰它。另请注意,协程通常(并非总是)"viral"。一旦将 yield from
添加到一个函数,它就会成为一个协程,此外,任何 调用 该协程的函数通常(尽管不总是)也需要成为一个协程.
何时使用asyncio.async
为什么协程并不总是流行?因为你实际上并不总是需要使用 yield from
来调用协程。如果你想调用协程并等待它完成,你只需要使用yield from
。如果你只是想在后台启动一个协程,你可以这样做:
asyncio.async(coroutine())
一旦控制 returns 到事件循环,这将安排 coroutine
到 运行;它不会等到 coroutine
完成后再继续下一行。一个普通的函数可以使用它来安排一个协程到 运行 而不必自己也成为一个协程。
您还可以使用此方法同时 运行 多个 coroutines
。所以,假设您有这两个协程:
@asyncio.coroutine
def coro1():
yield from asyncio.sleep(1)
print("coro1")
@asyncio.coroutine
def coro2():
yield from asyncio.sleep(2)
print("coro2")
如果你有这个:
@asyncio.coroutine
def main():
yield from coro1()
yield from coro2()
yield from asyncio.sleep(5)
asyncio.get_event_loop().run_until_complete(main())
1 秒后,将打印 "coro1"
。然后,再过两秒(总共三秒),将打印 "coro2"
,五秒后程序将退出,共计 8 秒 运行 时间。或者,如果您使用 asyncio.async
:
@asyncio.coroutine
def main():
asyncio.async(coro1())
asyncio.async(coro2())
yield from asyncio.sleep(5)
asyncio.get_event_loop().run_until_complete(main())
这将在一秒后打印 "coro1"
,一秒后打印 "coro2"
,程序将在 3 秒后退出,总共 运行 时间为 5 秒。
这对您的代码有何影响?
因此,按照这些规则,您的代码需要如下所示:
import asyncio
class ChatBot:
def __init__(self, reader, writer):
# __init__ shouldn't be a coroutine, otherwise you won't be able
# to instantiate ChatBot properly. So I've removed the code that
# used yield from, and moved it outside of __init__.
#conn = asyncio.open_connection(HOST, PORT, loop=loop)
#self.reader, self.writer = yield from conn
self.reader, self.writer = reader, writer
def send(self, msg):
# writer.write is not a coroutine, so you
# don't use 'yield from', and send itself doesn't
# need to be a coroutine.
self.writer.write(msg)
@asyncio.coroutine
def read(self):
msg = yield from self.reader.readline()
return msg
@asyncio.coroutine
def run(self):
while True:
msg = yield from self.read()
yield from self.parse(msg)
@asyncio.coroutine
def parse(self, msg):
if msg.startswith('PING'):
yield from self.some_work(msg)
elif msg.startswith('ERROR'):
yield from self.some_error()
else:
yield from self.server_log(msg)
@asyncio.coroutine
def some_work(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
@asyncio.coroutine
def some_error(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send().
@asyncio.coroutine
def server_log(self, msg):
# some work. It can call asynchronous function like I/O or long operation. It can use self.send()
@asyncio.coroutine
def main(host, port):
reader, writer = yield from asyncio.open_connection(HOST, PORT, loop=loop)
bot = ChatBot(reader, writer)
yield from bot.run()
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
还有一件事要记住——在函数前添加 yield from
并不能神奇地使该调用成为非阻塞的。添加 @asyncio.coroutine
装饰器也没有。只有当函数实际上直接或间接调用本机 asyncio
协程时,它们才是非阻塞的,这些协程使用非阻塞 I/O 并与 asyncio
事件循环集成。例如,您提到进行 REST API 调用。为了使那些 REST API 调用不阻塞事件循环,您需要使用 aiohttp
库或 asyncio.open_connection
。使用 requests
或 urllib
之类的东西会阻塞循环,因为它们没有与 `asyncio.