如何在python中多线程调用另一个端点方法?

时间:2017-08-10 16:45:08

标签: python multithreading flask cors

我有一个带有2个端点的api,一个是接收json的简单帖子,另一个是端点,根据jsons列表的长度多次调用1st,并将返回保存到列表中。

第一种方法

@app.route('/getAudience', methods=['POST', 'OPTIONS'])
def get_audience(audience_=None):
    try:
        if audience_:
            audience = audience_
        else:
            audience = request.get_json()
    except (BadRequest, ValueError):
        return make_response(jsonify(exception_response), 500)

    return get_audience_response(audience, exception_response)

第二种方法

@app.route('/getMultipleAudience', methods=['POST', 'OPTIONS'])
def get_multiple_audience():
    try:
        audiences = request.json
    except (BadRequest, ValueError):
        return make_response(jsonify(exception_response), 500)

    response = []
    for audience in audiences:
        new_resp = json.loads(get_audience(audience).data)
        response.append(new_resp)

    return make_response(jsonify(response))

我想调用第一个方法在第二个方法的列表中为每个对象启动一个线程,所以我尝试了这个:

def get_multiple_audience():
    with app.app_context():
        try:
            audiences = request.get_json()
        except (BadRequest, ValueError):
            return make_response(jsonify(exception_response), 500)

        for audience in audiences:
            thread = Thread(target=get_audience, args=audience)
            thread.start()

        thread.join()
        return make_response(jsonify(response))

得到了这个错误:

Exception in thread Thread-6:
Traceback (most recent call last):
  File "C:\Python27\lib\threading.py", line 801, in __bootstrap_inner
    self.run()
  File "C:\Python27\lib\threading.py", line 754, in run
    self.__target(*self.__args, **self.__kwargs)
  File "C:\Python27\lib\site-packages\flask_cors\decorator.py", line 123, in wrapped_function
    options = get_cors_options(current_app, _options)
  File "C:\Python27\lib\site-packages\flask_cors\core.py", line 286, in get_cors_options
    options.update(get_app_kwarg_dict(appInstance))
  File "C:\Python27\lib\site-packages\flask_cors\core.py", line 299, in get_app_kwarg_dict
    app_config = getattr(app, 'config', {})
  File "C:\Python27\lib\site-packages\werkzeug\local.py", line 347, in __getattr__
    return getattr(self._get_current_object(), name)
  File "C:\Python27\lib\site-packages\werkzeug\local.py", line 306, in _get_current_object
    return self.__local()
  File "C:\Python27\lib\site-packages\flask\globals.py", line 51, in _find_app
    raise RuntimeError(_app_ctx_err_msg)
RuntimeError: Working outside of application context.

然后我尝试修改第一种方法:

@app.route('/getAudience', methods=['POST', 'OPTIONS'])
def get_audience(audience_=None):
    with app.app_context():
        try:
        ...

并得到了同样的错误。任何人都可以给我一个提示,建议,最佳实践或解决方案吗?

1 个答案:

答案 0 :(得分:1)

这里有很多问题。首先,在这里:

for audience in audiences:
    thread = Thread(target=get_audience, args=audience)
    thread.start()

thread.join()

您只是在等待最后一个帖子完成。您应该有一个所有主题列表,并等待所有完成。

threads = []
for audience in audiences:
    thread = Thread(target=get_audience, args=audience)
    threads.append(thread)
    thread.start()

for thread in threads:
    thread.join()

第二个问题是你要归还一个response,它甚至不能在任何地方设置。但这并不是多线程的工作原理。您将拥有来自所有线程的多个结果,您必须跟踪它们。因此,您可以创建一个results数组来保存每个线程的返回值的答案。在这里,我将以一个简单的函数sum为例。

results = []
threads = []

def sum(a, b):
    results.append(a + b)

@app.route("/test")
def test():
    with app.app_context():
        for i in range(5):
            t = Thread(target=sum, args=(1, 2))
            threads.append(t)
            t.start()

        for t in threads:
            t.join()

        return jsonify(results)

这将很愉快,它将返回所有调用sum()函数的结果。

现在,如果我将总和更改为:

@app.route("/mysum/a/b")
def sum(a, b):
    results.append(a + b)
    return jsonify(a + b)

我会得到与你之前得到的类似的错误:即RuntimeError: Working outside of request context.,甚至认为返回值仍然是正确的:[3, 3, 3, 3, 3]。这里发生的是你的sum函数现在正在尝试返回一个烧瓶响应,但它位于自己的临时线程中,并且无法访问任何烧瓶。内部情境。你应该做的是永远return临时工作线程中的值,但是有一个池来存储它们以供将来参考。

但这并不意味着您无法拥有/mysum路线。确实,你可以,但逻辑必须分开。把它们放在一起:

results = []
threads = []

def sum(a, b):
    return a + b

def sum_worker(a, b):
    results.append(sum(a, b))

@app.route("/mysum/a/b")
def mysum(a, b):
    return jsonify(sum(a, b))

@app.route("/test")
def test():
    with app.app_context():
        for i in range(5):
            t = Thread(target=sum_worker, args=(1, 2))
            threads.append(t)
            t.start()

        for t in threads:
            t.join()

        return jsonify(results)

请注意,此代码非常粗糙,仅用于演示目的。我不建议在整个应用程序中创建全局变量,因此需要进行一些清理。