Как я могу отменить асинхронную задачу в торнадо с таймаутом?
Моя установка - сервер торнадо Python, который асинхронно обрабатывает задачи с ThreadPoolExecutor
, В некоторых условиях задача может превратиться в бесконечный цикл. С with_timeout
декоратор, мне удалось поймать исключение тайм-аута и вернуть клиенту результат ошибки. Проблема в том, что задача все еще выполняется в фоновом режиме. Как можно остановить выполнение задачи в ThreadPoolExecutor
? Или можно отменить Future
? Вот код, который воспроизводит проблему. Запустите код с библиотеками tornado 4 и concurrent.futures и перейдите по http://localhost:8888/test
from tornado.concurrent import run_on_executor
from tornado.gen import with_timeout
from tornado.ioloop import IOLoop
import tornado.web
from tornado import gen
from concurrent.futures import ThreadPoolExecutor
import datetime
MAX_WAIT_SECONDS = 10
class MainHandler(tornado.web.RequestHandler):
executor = ThreadPoolExecutor(2)
@run_on_executor
def test_func(self):
...
#infinite loop might be here
...
@tornado.gen.coroutine
def get(self):
future = self.test_func()
try:
result_search_struct = yield with_timeout(datetime.timedelta(seconds=MAX_WAIT_SECONDS), future )
self.write({'status' : 0})
self.finish()
except Exception, e:
#how to cancel the task here if it was timeout
future.cancel() # <-- Does not work
self.write({'status' : 100})
self.finish()
application = tornado.web.Application([
(r"/test", MainHandler),
])
application.listen(8888)
IOLoop.instance().start()
1 ответ
Future
Сами экземпляры не могут быть отменены, когда они фактически выполняются, они могут быть отменены, только если они находятся в состоянии ожидания. Это отмечено в документах:
отменить()
Попытка отменить звонок. Если вызов в данный момент выполняется и не может быть отменен, метод вернется
False
в противном случае вызов будет отменен и метод вернетсяTrue
,
Таким образом, единственный способ прервать метод, который вы запускаете в фоновом режиме, - это фактически вставить логику в потенциально бесконечный цикл, чтобы его можно было прервать, когда вы скажете это. С вашим примером вы могли бы использовать threading.Event
:
class MainHandler(tornado.web.RequestHandler):
executor = ThreadPoolExecutor(2)
@run_on_executor
def test_func(self, event):
i = 0
while not event.is_set():
print i
i = i + 1
@tornado.gen.coroutine
def get(self):
event = threading.Event()
future = self.test_func(event)
try:
result_search_struct = yield with_timeout(datetime.timedelta(seconds=MAX_WAIT_SECONDS), future )
self.write({'status' : 0})
self.finish()
except Exception, e:
future.cancel() # Might not work, depending on how busy the Executor is
event.set()
self.write({'status' : 100})
self.finish()
application = tornado.web.Application([
(r"/test", MainHandler),
])