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

如何在python中多线程调用另一个端点方法?,python,multithreading,flask,cors,Python,Multithreading,Flask,Cors,我有一个带有两个端点的api,一个是接收json的简单post,另一个是根据json列表的长度多次调用第一个端点并将返回保存到列表的端点 第一种方法 @app.route('/getAudience', methods=['POST', 'OPTIONS']) def get_audience(audience_=None): try: if audience_: audience = audience_ else:

我有一个带有两个端点的api,一个是接收json的简单post,另一个是根据json列表的长度多次调用第一个端点并将返回保存到列表的端点

第一种方法

@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:
        ...

得到了同样的错误。有谁能给我一个提示、建议、最佳实践或解决方案吗?

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

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()
第二个问题是,您返回的是单个
响应
,它甚至没有设置在任何位置。但多线程不是这样工作的。您将从所有线程获得多个结果,并且必须跟踪它们。因此,您可以创建一个
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:在请求上下文之外工作。
,甚至认为返回值仍然正确:
[3,3,3,3]
。这里发生的事情是,您的
sum
函数现在正试图返回一个flash响应,但它驻留在自己的临时线程中,无法访问flash的任何内部上下文。您应该做的是永远不要在临时工作线程中返回值,而是使用一个池来存储它们以供将来参考

但这并不意味着你不能有一条
/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)

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

如果要测试web服务,最好的方法是使用请求创建单独/独立的应用程序。