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

如何在Flask模板中点击按钮检测并终止指定多进程对象?

嘿,我来帮你搞定这个进程检测和终止的需求!你已经实现了进程启动和流式输出到模板的功能,现在要加的这个交互功能,咱们可以分成后端逻辑处理和前端交互实现两部分来做,一步步来:

核心思路说明

首先要明确:Flask的请求是独立的,直接在请求间共享multiprocessing.Process对象是不可行的,因为Flask默认是多线程/多进程运行的。所以咱们需要一个全局的线程安全存储来保存正在运行的进程,同时用Session来区分不同用户的进程,避免互相干扰。

后端实现步骤

1. 初始化全局存储和锁

先在你的Flask app初始化部分,添加全局的进程存储和线程锁,保证并发修改时的安全:

from flask import Flask, request, jsonify, session, render_template
import multiprocessing
import threading

app = Flask(__name__)
app.secret_key = "your_secure_secret_key_here"  # 必须设置,用来维护Session

# 全局存储运行中的进程,键用Session ID区分不同用户
running_processes = {}
# 线程锁,保护全局字典的并发操作
process_lock = threading.Lock()

2. 修改原有任务启动逻辑

在你原来的/tasks/<task>路由里,启动训练进程时,要把Process对象(如果需要优雅终止,还可以加一个终止信号Event)存入全局字典:

def train_task(arch_path, data_path, stop_event):
    """修改你的训练函数,加入终止信号检测"""
    try:
        # 你的训练逻辑,每一步循环都检查终止信号
        for step in range(total_steps):
            if stop_event.is_set():
                print("收到终止信号,停止训练")
                break
            # 执行训练步骤、输出日志等操作
            ...
    except Exception as e:
        print(f"训练出错:{e}")

@app.route('/tasks/<task>', methods=['GET', 'POST'])
def tasks(task):
    if request.method == 'POST':
        if task == 'training':
            # 获取你的参数
            arch_name = request.form.get('architecture_name')
            arch_path = create_path(app.config['ARCHITECTURES_DIRECTORY'], f"{arch_name}.json")
            data_path = create_path(app.config['DATA_DIRECTORY'], ...)  # 你的数据路径

            # 创建终止信号Event和进程对象
            stop_event = multiprocessing.Event()
            train_process = multiprocessing.Process(
                target=train_task,
                args=(arch_path, data_path, stop_event)
            )
            train_process.start()

            # 把进程和终止信号存入全局字典,用Session ID作为键
            with process_lock:
                running_processes[session.sid] = {
                    "process": train_process,
                    "stop_event": stop_event
                }

            # 你的原有流式输出逻辑保持不变
            return ...  # 比如返回流式响应的生成器

    # GET请求返回模板
    return render_template('tasks.html')

3. 添加进程状态检测接口

写一个GET接口,用来查询当前用户的进程是否在运行:

@app.route('/check_process_status', methods=['GET'])
def check_process_status():
    with process_lock:
        proc_data = running_processes.get(session.sid)
    
    if proc_data and proc_data['process'].is_alive():
        return jsonify({"is_running": True})
    else:
        # 进程已结束,从全局字典中清理
        with process_lock:
            running_processes.pop(session.sid, None)
        return jsonify({"is_running": False})

4. 添加进程终止接口

写一个POST接口,用来终止当前用户的运行中进程:

@app.route('/terminate_process', methods=['POST'])
def terminate_process():
    with process_lock:
        proc_data = running_processes.get(session.sid)
    
    if not proc_data or not proc_data['process'].is_alive():
        return jsonify({"success": False, "message": "进程未在运行"})
    
    # 发送优雅终止信号,等待进程退出
    proc_data['stop_event'].set()
    proc_data['process'].join(timeout=5)  # 设置超时,避免无限等待
    
    # 如果超时还没退出,强制杀死进程
    if proc_data['process'].is_alive():
        proc_data['process'].terminate()
        proc_data['process'].join()
    
    # 清理全局存储
    with process_lock:
        running_processes.pop(session.sid, None)
    
    return jsonify({"success": True, "message": "进程已终止"})
前端模板修改

在你的Jinja模板(比如tasks.html)里,添加检测按钮、终止按钮和状态提示区域,用AJAX实现无刷新交互:

<!-- 你的原有流式输出区域 -->
<div id="stream-output">
    <!-- 这里是流式输出的内容 -->
</div>

<!-- 新增进程控制区域 -->
<div class="process-controls">
    <button id="check-status-btn">检测进程状态</button>
    <button id="terminate-btn" disabled>终止进程</button>
    <div id="status-message" class="status-text"></div>
</div>

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script>
let statusCheckInterval;

$(document).ready(function() {
    // 检测状态按钮点击事件
    $('#check-status-btn').on('click', function() {
        checkProcessStatus();
        // 启动定时检测(每2秒一次),避免重复启动
        if (!statusCheckInterval) {
            statusCheckInterval = setInterval(checkProcessStatus, 2000);
        }
    });

    // 终止进程按钮点击事件
    $('#terminate-btn').on('click', function() {
        $(this).prop('disabled', true);
        $.post('/terminate_process', function(response) {
            $('#status-message').text(response.message);
            // 停止定时检测
            clearInterval(statusCheckInterval);
            statusCheckInterval = null;
        }).fail(function() {
            $('#status-message').text('终止进程失败,请重试');
            $('#terminate-btn').prop('disabled', false);
        });
    });
});

// 封装检测进程状态的函数
function checkProcessStatus() {
    $.get('/check_process_status', function(response) {
        if (response.is_running) {
            $('#status-message').text('✅ 进程正在运行');
            $('#terminate-btn').prop('disabled', false);
        } else {
            $('#status-message').text('❌ 进程已结束或未启动');
            $('#terminate-btn').prop('disabled', true);
            // 进程结束后停止定时检测
            clearInterval(statusCheckInterval);
            statusCheckInterval = null;
        }
    }).fail(function() {
        $('#status-message').text('检测状态失败,请重试');
    });
}
</script>
关键注意事项
  • 线程安全:一定要用threading.Lock保护全局的running_processes字典,避免多请求并发修改导致的异常。
  • 用户隔离:用session.sid作为进程存储的键,确保每个用户只能操作自己启动的进程,不会影响其他用户。
  • 优雅终止:尽量用multiprocessing.Event发送终止信号,让进程可以自行清理资源,而不是直接用terminate()强制杀死,避免内存泄漏或数据损坏。
  • 资源清理:进程结束后,一定要从全局字典中移除对应的条目,避免内存占用过高。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 06:30:32