在后台线程上执行长时间运行的计算密集型操作

时间:2019-03-01 12:15:21

标签: python tornado

我已经在Python Tornado框架中编写了REST API,该API可以根据给定的段落预测问题的答案。


这是Tornado处理程序的Python代码:

def post(self):
    """
    This function predicts the response from the pre-trained Allen model
    """    
    try:
        request_payload = tornado.escape.json_decode(self.request.body)

        if (request_payload is None):
            return self._return_response(self, { "message": "Invalid request!" }, 400)

        context = request_payload["context"]
        question = request_payload["question"]

        if(context is None or not context):
            return self._return_response(self, { "message": "Context is not provided!" }, 400)

        if(question is None or not question):
            return self._return_response(self, { "message": "Question is not provided!" }, 400)

        # Compute intensive operation which blocks the main thread
        answer_prediction = predictor.predict(passage=str(context), question=str(question))
        best_answer = answer_prediction["best_span_str"] or "Sorry, no answer found for your question!"

        return self._return_response(self, { "answer": best_answer }, 200)

    except KeyError:
        #Return bad request if any of the keys are missing
        return self._return_response(self, { "message": 'Some keys are missing from the request!' }, 400)

    except json.decoder.JSONDecodeError:
        return self._return_response(self, { "message": 'Cannot decode request body!' }, 400)

    except Exception as ex:
        return self._return_response(self, { "message": 'Could not complete the request because of some error at the server!', "cause": ex.args[0], "stack_trace": traceback.format_exc(sys.exc_info()) }, 500)

问题是该行:

  

answer_prediction = dictor.predict(passage = str(context),   问题= str(问题))

阻塞主线程以处理传入的请求,并等待该长时间运行的操作完成,同时阻塞其他请求并有时使当前请求超时。


我已阅读this答案,详细说明了将长期运行的操作排入队列的解决方案,但我没有得到。

此外,由于Python的GIL,一个线程只能同时运行,这迫使我产生一个单独的进程来处理它,因为进程成本高昂,是否有可行的解决方案来解决我的问题以及如何解决这个问题那种情况。

这是我的问题:

  • 如何将安全的计算密集型操作卸载到后台 线程
  • 如何正常处理超时和异常
  • 如何维护队列结构以检查长时间运行的操作是否已完成。

2 个答案:

答案 0 :(得分:1)

在单独的线程中运行阻止代码。使用IOLoop.run_in_executor

示例:

from functools import partial

async def post(self):
    ...

    # create a partial object with the keyword arguments
    predict_partial = partial(predictor.predict, passage=str(context), question=str(question))

    answer_prediction = await IOLoop.current().run_in_executor(None, predict_partial)

    ...

答案 1 :(得分:0)

我认为您应该将此API调用转换为异步调用,并立即使用令牌返回给调用者。

令牌稍后将使用该令牌以检查(使用另一个API调用)操作是否完成。