¿Cómo puedo cancelar una tarea asíncrona colgada en un tornado, con un tiempo de espera?

Mi configuración es Python Tornado Server, que procesa tareas de forma asíncrona con un ThreadPoolExecutor . En algunas condiciones, la tarea puede convertirse en un bucle infinito. Con el decorador with_timeout , he logrado detectar la excepción de tiempo de espera y devolver un resultado de error al cliente. El problema es que la tarea todavía se está ejecutando en segundo plano. ¿Cómo es posible detener la ejecución de la tarea en el ThreadPoolExecutor ? ¿O es posible cancelar el Future ? Aquí está el código que reproduce el problema. Ejecute el código con las bibliotecas tornado 4 y concurrent.futures y vaya a 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() 

Future instancias Future no se pueden cancelar una vez que se ejecutan, solo se pueden cancelar si están pendientes. Esto se nota en los documentos :

cancelar()

Intenta cancelar la llamada. Si la llamada se está ejecutando actualmente y no se puede cancelar, el método devolverá False ; de lo contrario, la llamada se cancelará y el método devolverá True .

Por lo tanto, la única manera de abortar el método que está ejecutando en segundo plano es insertar la lógica en su bucle potencialmente infinito para que pueda abortarse cuando se lo indique. Con tu ejemplo, podrías usar un threading.Event . 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), ])