You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask线程函数致应用挂起,寻求排查解决方法

解决Flask异步执行慢任务时页面挂起的问题

Hey there! Let's break down what's causing your Flask app to hang even after starting a thread, and fix it so you can show that "Loading..." interface properly.

首先,你的代码里有几个关键问题

  1. 线程参数传递错误:你给async_slow_function传的arguments是""(空字符串),但Thread的args参数需要是一个可迭代的元组。当你传字符串时,Python会把它拆成单个字符作为参数传给tests.strobe,如果这个函数不接受参数,会直接抛出异常,导致线程崩溃或者卡住。
  2. 缺少任务状态跟踪:你启动线程后直接返回index(),但前端完全不知道任务什么时候完成。而且Flask的请求是无状态的,你没有办法让用户知道测试是否正在运行、已经完成还是失败了。
  3. 线程未设置为守护线程:如果线程没有标记为守护线程,它可能会阻止Flask应用正常退出,尤其是在debug模式下。

解决方案:添加任务状态跟踪 + 前端轮询

我们需要做三件事:

  • 修复线程参数传递
  • 用一个存储来跟踪每个任务的状态
  • 前端通过AJAX轮询任务状态,显示“加载中”直到任务完成

步骤1:修改Flask后端代码

from flask import Flask, render_template, request, jsonify
import tests
from threading import Thread
import uuid

app = Flask(__name__)

# 用全局字典存储任务状态(生产环境建议用Redis/数据库,这里新手先凑合用)
task_status = {}

def run_slow_task(task_id, test, arguments):
    try:
        task_status[task_id] = {"status": "running"}
        # 执行慢测试
        result = test(*arguments)
        task_status[task_id] = {"status": "completed", "result": result}
    except Exception as e:
        task_status[task_id] = {"status": "failed", "error": str(e)}

def async_slow_function(task_id, test, arguments):
    # 设置线程为守护线程,避免阻止应用退出
    thr = Thread(target=run_slow_task, args=(task_id, test, arguments))
    thr.daemon = True
    thr.start()
    print(f"Thread starting for task {task_id}...")
    return thr

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/start-strobe-test')
def start_strobe_test():
    # 生成唯一任务ID
    task_id = str(uuid.uuid4())
    # 启动线程,注意args是元组,如果无参数就传()
    async_slow_function(task_id, tests.strobe, ())
    return jsonify({"task_id": task_id})

@app.route('/check-task-status/<task_id>')
def check_task_status(task_id):
    status = task_status.get(task_id, {"status": "not_found"})
    return jsonify(status)

if __name__ == '__main__':
    app.run(debug=True, threaded=True, host='0.0.0.0')

步骤2:创建前端模板templates/index.html

这个模板会显示“加载中...”,并定期请求后端检查任务状态:

<!DOCTYPE html>
<html>
<head>
    <title>QA Burn-In Test</title>
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
</head>
<body>
    <h1>QA套件老化测试</h1>
    <button id="start-test-btn">启动测试</button>
    <div id="status-message" style="display: none; color: #666; font-size: 18px;">加载中...</div>
    <div id="result-message" style="display: none; margin-top: 15px; padding: 10px; border-radius: 4px;"></div>

    <script>
        $(document).ready(function() {
            $("#start-test-btn").click(function() {
                // 禁用按钮,显示加载状态
                $(this).prop("disabled", true);
                $("#status-message").show();
                $("#result-message").hide();

                // 启动测试
                $.get("/start-strobe-test", function(data) {
                    const taskId = data.task_id;
                    // 每2秒检查一次任务状态
                    const interval = setInterval(function() {
                        $.get(`/check-task-status/${taskId}`, function(status) {
                            if (status.status === "completed") {
                                clearInterval(interval);
                                $("#status-message").hide();
                                $("#result-message").text(`测试完成!结果:${status.result}`).css("background", "#e8f5e9").show();
                                $("#start-test-btn").prop("disabled", false);
                            } else if (status.status === "failed") {
                                clearInterval(interval);
                                $("#status-message").hide();
                                $("#result-message").text(`测试失败!错误:${status.error}`).css("background", "#ffebee").show();
                                $("#start-test-btn").prop("disabled", false);
                            }
                        });
                    }, 2000);
                });
            });
        });
    </script>
</body>
</html>

关键细节解释

  • 任务ID:用UUID生成唯一标识,确保每个任务都能被准确跟踪。
  • 线程守护设置:thr.daemon = True让线程在主应用退出时自动终止,避免遗留线程占用资源。
  • 前端轮询:通过AJAX每隔2秒请求一次状态接口,这样用户页面不会挂起,能实时看到状态变化。
  • 异常处理:在异步任务函数里捕获异常,把错误信息存入任务状态,前端能直观显示失败原因。

生产环境注意事项

  • 全局字典task_status在Flask用多进程模式(比如Gunicorn多worker)时会失效,因为每个worker有自己的内存空间,这时候需要用Redis、Memcached或者数据库来存储任务状态。
  • 如果任务需要更实时的状态推送,可以考虑用WebSocket(比如Flask-SocketIO)替代轮询,减少服务器请求压力。

内容的提问来源于stack exchange,提问作者ltd9938

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:53:17