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

多线程运行多个Flask类实例仅第一个生效的解决方案

问题根因

多Flask实例启动失败核心是代码逻辑错误,导致第一个实例启动后直接阻塞主线程,后续实例根本没有执行机会:

  • 服务启动逻辑错放在类构造函数__init__中
    构造函数会在类实例化时于当前线程同步执行,你写threading.Thread(target=runFlaskApp(xxx))时,runFlaskApp(xxx)这一步实例化就会直接触发app.run()阻塞主线程,根本不会把启动逻辑交给子线程调度,后面三个实例的实例化代码永远不会运行。
  • 多线程类用法冲突
    你的hook_server类已经继承了threading.Thread,不需要再在外层额外套一层threading.Thread,重复包装会导致线程调度逻辑完全失效。
  • 额外配置错误:第四个实例的token和第三个重复写为GW3,后续路由注册会冲突。
修复方案

调整类结构,把Flask服务启动逻辑从构造函数挪到线程类标准的run()方法中,修正启动逻辑即可:

  1. 修正类定义部分,补全线程类初始化调用,把服务启动逻辑移到run()方法:
import os
import sys
import signal
import logging
import threading

from flask import Flask, request, jsonify

# Defined by team SNL
from Constants import *
from Utils import *

logger = logging.getLogger(__name__)
handler = logging.StreamHandler(stream=sys.stdout)

# Parent class for running hook_server (with some preprocessing).
class hook_server(threading.Thread):
    def __init__ (self, token, port):
        # 调用父类Thread的初始化方法,原代码漏写
        super().__init__()
        # Saved value from gateway response.
        self.value = []
        
        self.token = token
        self.port = port
        
        # Setup for a Flask application instants.
        self.server_app = Flask(__name__, static_folder='./media')
        self.server_app.debug = False
        
        # Ignore web-hook server tracking information.
        log = logging.getLogger('werkzeug')
        log.disabled = True

        @self.server_app.route('/' + self.token, methods=['GET', 'POST'])
        def index1():
            # If 'GET' request.
            if request.method == 'GET':
                # JSON LONG convert to the dictionary.
                content = request.args.to_dict()
                
            # If 'POST' request.
            elif request.method == 'POST':
                # The below conditions are always True. Because we set-up the transmit data type to JSON-LONG.
                if request.is_json is True:
                    
                    # Read the JSON-LONG data.
                    content = request.get_json()    

                    # Received beacon information (dict) per time slot (1 sec).
                    MAX_EXTRACT_LOOP = len(content)
                    
                    # Unwrapping of i-beacon dictionary <dict>.
                    Pkt = 0
                    for i in range(0, MAX_EXTRACT_LOOP):
                        # <list of list>
                        self.value = list(func_combine(content[i].get('mac'), int(func_kalman_filter(content[i].get('rssi'))), content[i].get('ibeaconTxPower')))
                        # Get received beacon packet length.
                        Pkt = len(self.value[0])
                    
                    # Export acquired beacon information from each gateway.
                    if (EXPORT_ACTIVE and Pkt % MAX_PKT == 0):
                        # Save the received beacon information every MAX_Pkt '5000'.
                        func_export(self.token, self.value[0], self.value[1], self.value[2])
                        del self.value[0][0:-AT_LEAST_INFO]      # Save the last received 10 beacon MAC.
                        del self.value[1][0:-AT_LEAST_INFO]      # Save the last received 10 beacon RSSI.
                        del self.value[2][0:-AT_LEAST_INFO]      # Save the last received 10 beacon TX POWER.
        
                    # Only remove tuple of list elements.
                    elif (Pkt % MAX_PKT == 0):
                        del self.value[0][0:-AT_LEAST_INFO]      # Save the last received 10 beacon MAC.
                        del self.value[1][0:-AT_LEAST_INFO]      # Save the last received 10 beacon RSSI.
                        del self.value[2][0:-AT_LEAST_INFO]      # Save the last received 10 beacon TX POWER.
                        
            # Return the binded value <dict of list of list> like that {"GW1":[["AC233FAA45E5"], [-29], [-59]]}.
            return jsonify({"{}".format(self.token): self.value})

# A child class that inherits and executes hook_server.
class runFlaskApp(hook_server):
    def __init__(self, token, port):
        # 仅做初始化继承,不在构造函数里启动服务
        super().__init__(token, port)
        
    # 线程启动后自动执行run方法内的逻辑
    def run(self):
        try:
            # Where host IP 0.0.0.0 that grants external access.
            print('[{}] Server open successfully !!'.format(self.token))
            self.server_app.run(host='0.0.0.0', port=self.port, use_reloader=False, threaded=True)
        except:
            print('[{}] Server occured error !!'.format(self.token))
            os.kill(os.getpid(), signal.SIGTERM)
  1. 修正主函数启动逻辑,去掉重复的线程包装,修正重复的token配置:
if __name__ == '__main__':
    thread_pool = []
    # 直接实例化自定义线程类,不需要额外套threading.Thread
    G1 = runFlaskApp(token = 'GW1', port = 2998)
    G2 = runFlaskApp(token = 'GW2', port = 2999)
    G3 = runFlaskApp(token = 'GW3', port = 3000)
    G4 = runFlaskApp(token = 'GW4', port = 3001) # 修正之前重复写为GW3的错误
    
    for server in [G1, G2, G3, G4]:
        server.start()
        thread_pool.append(server)
    
    # 阻塞等待所有服务线程运行
    for t in thread_pool:
        t.join()
排查提示
  • 如果修正后仍有实例启动失败,先检查2998-3001端口是否被其他进程占用:Linux/macOS系统执行netstat -tulpn查看,Windows系统执行netstat -ano查看。
  • 调试阶段可以先单独启动单个实例,确认路由、依赖逻辑正常后再批量启动多实例,方便快速定位问题。
  • 注意Flask自带的run()方法仅适合开发调试使用,生产环境多实例部署建议换成gunicorn等专业WSGI服务器,稳定性和并发能力更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 13:15:48