Python 试图实施2“;“线程”;使用'asyncio'模块
我以前在Python中使用过线程,但决定尝试一下Python 试图实施2“;“线程”;使用'asyncio'模块,python,python-3.x,python-asyncio,Python,Python 3.x,Python Asyncio,我以前在Python中使用过线程,但决定尝试一下asyncio模块,特别是因为您可以取消正在运行的任务,这看起来是一个很好的细节。然而,由于某种原因,我不能把我的头围绕着它 以下是我想要实现的内容(如果我使用了错误的术语,很抱歉): 一个downloader线程,它每x秒下载一个相同的文件,对照上一次下载检查其哈希值,如果不同,则保存它 在后台运行的webserver线程,允许控制downloader线程(暂停、列出、停止) 我对Web服务器使用了aiohttp 这就是我到目前为止所做的:
asyncio
模块,特别是因为您可以取消正在运行的任务,这看起来是一个很好的细节。然而,由于某种原因,我不能把我的头围绕着它
以下是我想要实现的内容(如果我使用了错误的术语,很抱歉):
- 一个
线程,它每x秒下载一个相同的文件,对照上一次下载检查其哈希值,如果不同,则保存它downloader
- 在后台运行的
线程,允许控制webserver
线程(暂停、列出、停止)downloader
aiohttp
这就是我到目前为止所做的:
class aiotest():
def __init__(self):
self._dl = None # downloader future
self._webapp = None # web server future
self.init_server()
def init_server(self):
print('Setting up web interface')
app = web.Application()
app.router.add_route('GET', '/stop', self.stop)
print('added urls')
self._webapp = app
@asyncio.coroutine
def _downloader(self):
while True:
try:
print('Downloading and verifying file...')
# Dummy sleep - to be replaced by actual code
yield from asyncio.sleep(random.randint(3,10))
# Wait a predefined nr of seconds between downloads
yield from asyncio.sleep(30)
except asyncio.CancelledError:
break
@asyncio.coroutine
def _supervisor(self):
print('Starting downloader')
self._dl = asyncio.async(self._downloader())
def start(self):
loop = asyncio.get_event_loop()
loop.run_until_complete(self._supervisor())
loop.close()
@asyncio.coroutine
def stop(self):
print('Received STOP')
self._dl.cancel()
return web.Response(body=b"Stopping... ")
此类由以下人员调用:
t = aiotest()
t.start()
这当然不行,我觉得这是一段可怕的代码
我不清楚的是:
- 我在
方法中停止stop()
,但是如何停止Web服务器(例如在downloader
方法中)shutdown()
下载程序是否需要一个新的事件循环,或者我是否可以使用
返回的循环asyncio.get_event_loop()
- 我真的需要类似于
主管的东西来实现我想要实现的东西吗?这看起来很笨重。我如何让
继续运行,而不是像现在这样执行一次就结束supervisor
asyncio
是否应该取代threading
模块(将来)?或者每个应用程序都有自己的应用程序
我感谢所有的提示、评论和澄清 当前代码不起作用的原因:
- 您正在运行事件循环,直到
完成<代码>自我。_supervisor()创建任务(立即发生)并立即完成self.\u supervisor()
- 您正在尝试运行事件循环,直到
完成,但如何以及何时启动服务器?我认为事件循环应该一直运行,直到服务器停止<代码>\u主管或其他内容可以作为任务添加(到同一事件循环)\u supervisor
已经具有启动服务器和事件循环的功能-aiohttp
web。运行应用程序,但我们可以做到
主管
asyncio
帮助您运行不同的
函数在单个进程的单个线程中并行。这就是为什么
asyncio是如此的酷和快速。你的一些同步代码与你的线程
可以使用asyncio及其协程重写。此外,asyncio可以
使用线程和进程。
在您仍然需要线程和进程的情况下,它会很有用:下面是一个例子- 当我们讨论非线程的异步协程时,最好使用术语
而不是coroutine
thread
- 如果使用Python 3.5,则可以使用
/async
而不是wait
/coroutine
从
http://localhost:8080/stop
,请参阅控制台,打开http://localhost:8080/start
,请参见控制台,键入CTRL+C
import asyncio
import random
from contextlib import suppress
from aiohttp import web
class aiotest():
def __init__(self):
self._webapp = None
self._d_task = None
self.init_server()
# SERVER:
def init_server(self):
app = web.Application()
app.router.add_route('GET', '/start', self.start)
app.router.add_route('GET', '/stop', self.stop)
app.router.add_route('GET', '/kill_server', self.kill_server)
self._webapp = app
def run_server(self):
# Create server:
loop = asyncio.get_event_loop()
handler = self._webapp.make_handler()
f = loop.create_server(handler, '0.0.0.0', 8080)
srv = loop.run_until_complete(f)
try:
# Start downloader at server start:
asyncio.async(self.start(None)) # I'm using controllers here and below to be short,
# but it's better to split controller and start func
# Start server:
loop.run_forever()
except KeyboardInterrupt:
pass
finally:
# Stop downloader when server stopped:
loop.run_until_complete(self.stop(None))
# Cleanup resources:
srv.close()
loop.run_until_complete(srv.wait_closed())
loop.run_until_complete(self._webapp.shutdown())
loop.run_until_complete(handler.finish_connections(60.0))
loop.run_until_complete(self._webapp.cleanup())
loop.close()
@asyncio.coroutine
def kill_server(self, request):
print('Server killing...')
loop = asyncio.get_event_loop()
loop.stop()
return web.Response(body=b"Server killed")
# DOWNLOADER
@asyncio.coroutine
def start(self, request):
if self._d_task is None:
print('Downloader starting...')
self._d_task = asyncio.async(self._downloader())
return web.Response(body=b"Downloader started")
else:
return web.Response(body=b"Downloader already started")
@asyncio.coroutine
def stop(self, request):
if (self._d_task is not None) and (not self._d_task.cancelled()):
print('Downloader stopping...')
self._d_task.cancel()
# cancel() just say task it should be cancelled
# to able task handle CancelledError await for it
with suppress(asyncio.CancelledError):
yield from self._d_task
self._d_task = None
return web.Response(body=b"Downloader stopped")
else:
return web.Response(body=b"Downloader already stopped or stopping")
@asyncio.coroutine
def _downloader(self):
while True:
print('Downloading and verifying file...')
# Dummy sleep - to be replaced by actual code
yield from asyncio.sleep(random.randint(1, 2))
# Wait a predefined nr of seconds between downloads
yield from asyncio.sleep(1)
if __name__ == '__main__':
t = aiotest()
t.run_server()
您的
下载程序是否像在传统线程中一样阻塞?或者它会使用所有异步调用吗?@cpburnz我最初使用的是请求
(如果我读得正确,它会被阻塞),但也可以使用aiohttp
。它不会下载多个文件(只有一个),而且文件本身相对较小(<200KB)。@Kristof关于答案,你有什么问题吗?尽管问吧。@German你真的帮了我很多忙,我已经在asyncio
上读了不少书,慢慢地开始掌握窍门了。非常感谢您花时间重写我的代码!如果我想通过HTTP调用关闭服务器(并完全停止脚本),就像我使用CTRL-C
中断一样,我该怎么办?我的每一次尝试都会导致运行时错误:事件循环正在运行。
@Kristof您的服务器是处理请求的事件循环。您可以运行服务器使其永远工作,但可以使用键盘中断来停止它。如果您想在另一个协同程序或函数中停止它,还可以调用loop.close()
-它将执行与键入CTRL+C时几乎相同的操作。例如,现在您可以使用停止服务器http://localhost:8080/kill_server
。还有一个注意事项:我知道最好将downloader stop放置在finally
块中(只要您打算以不同的方式停止服务器)。