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

树莓派Flask线程运行时串口读取线程无法并行的解决咨询

问题分析与解决方案

核心问题

你的代码存在两个关键问题导致串口线程无法正常运行:

  1. Flask启动阻塞主线程:socketio.run()是阻塞式调用,你把它直接写在FlaskServer类的顶层代码中,当创建FlaskServer实例时,这行代码会立即执行,卡住主线程,导致串口线程无法正常并行执行。
  2. 线程目标设置错误:FlaskThread的target设为Flask(类实例化),这不是一个可调用的方法,正确做法是指向Flask服务的启动方法。

另外还有两个次要问题:

  • SocketIO事件处理中引用了未定义的cycle变量,应该改为serialMessage。
  • 全局变量serialMessage没有线程安全保护,多线程读写可能引发数据混乱。

修正后的代码

from flask import Flask, render_template, request
from flask_socketio import SocketIO, emit
from threading import Thread, Lock
import time
import serial

# 全局变量+线程锁,保证多线程访问安全
serialMessage = ""
message_lock = Lock()

class SerialReadProgram:  
    def __init__(self):
        self._running = True
        
    def terminate(self):  
        self._running = False  

    def run(self):
        global serialMessage
        try:
            ser = serial.Serial("/dev/ttyS0", 9600, timeout=0.1)
            print("Serial Start")
            while self._running:
                # 读取串口数据
                data = ser.read()             
                if data:
                    time.sleep(0.03)
                    data_left = ser.inWaiting()             
                    data += ser.read(data_left)
                    # 加锁更新全局变量
                    with message_lock:
                        serialMessage = data.decode('utf-8', errors='ignore')
                    print(serialMessage) 
                time.sleep(0.01)  # 降低循环频率,减少资源占用
        except Exception as e:
            print(f"串口错误: {e}")
        finally:
            if 'ser' in locals():
                ser.close()

class FlaskServer:
    def __init__(self):
        self.app = Flask(__name__)
        self.socketio = SocketIO(self.app, cors_allowed_origins='*')
        self._setup_routes()
        self._setup_socket_events()

    def _setup_routes(self):
        @self.app.route('/', methods=('GET', 'POST'))
        def home():
            if request.method == 'POST':
                message = request.form['message']
                print(message)
            return render_template('index.html')

    def _setup_socket_events(self):
        @self.socketio.on('connect')
        def test_connect():
            emit('after connect', {'data':'Connected'})

        @self.socketio.on('Fetch For Data')
        def value_changed():
            # 加锁读取全局变量
            with message_lock:
                current_message = serialMessage
            emit('Updated Message', {'data': current_message}, broadcast=True)

    def run(self):
        # 启动Flask-SocketIO服务
        self.socketio.run(self.app, host='0.0.0.0', allow_unsafe_werkzeug=True)

# 启动串口线程
serial_read = SerialReadProgram()
serial_thread = Thread(target=serial_read.run, daemon=True)
serial_thread.start()

# 启动Flask线程
flask_server = FlaskServer()
flask_thread = Thread(target=flask_server.run, daemon=True)
flask_thread.start()

# 主线程保持运行,防止程序退出
while True:
    time.sleep(1)

关键修改说明

  • 重构FlaskServer类:把路由和SocketIO事件处理封装到类的方法中,将socketio.run()移到run()方法里,避免实例化类时直接阻塞主线程。
  • 线程目标修正:Flask线程的target指向flask_server.run方法,确保线程启动时执行服务启动逻辑。
  • 线程安全处理:添加Lock对象保护serialMessage的读写,避免多线程同时操作导致数据异常。
  • 串口读取优化:添加异常捕获和串口关闭逻辑,防止程序崩溃;增加timeout和循环休眠,减少资源占用。
  • 修复变量错误:把SocketIO事件中的cycle改为serialMessage,避免未定义变量报错。

运行说明

  1. 确保树莓派已安装依赖:pip install flask flask-socketio pyserial
  2. 准备好templates/index.html页面,通过SocketIO监听Updated Message事件来接收串口数据并展示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 05:27:22