如何在 Flask 应用中正确启动与停止 USB 设备监控进程

陌丽君_5382

陌丽君_5382

2026-08-06

413人浏览

原创

如何在 Flask 应用中正确启动与停止 USB 设备监控进程

本文详解如何使用 pyudev.MonitorObserver 实现 Flask 中 USB 监控的可控启停,避免线程阻塞和状态不同步问题,通过线程安全封装 + 事件驱动设计确保 start/stop 可靠生效。

本文详解如何使用 `pyudev.monitorobserver` 实现 flask 中 usb 监控的可控启停,避免线程阻塞和状态不同步问题,通过线程安全封装 + 事件驱动设计确保 `start/stop` 可靠生效。

在 Flask 应用中集成 USB 设备热插拔监控(如使用 pyudev)时,一个常见误区是:手动创建额外线程并依赖全局标志(如 running = False)来控制 MonitorObserver 的生命周期。这不仅冗余,还极易导致竞态条件——因为 pyudev.MonitorObserver 本身已是独立线程,其 start() 和 stop() 方法已内置线程同步逻辑。强行嵌套线程 + while running 循环反而会干扰原生观察者机制,造成 stop() 不响应、事件仍持续触发等问题。

✅ 正确做法是:直接管理 MonitorObserver 实例的生命周期,无需额外线程或轮询循环。以下为精简、健壮、可直接运行的实现方案:

✅ 核心设计原则

  • 将 MonitorObserver 封装为单例类(如 MyObserver),提供线程安全的 start() / stop() 接口;
  • 使用 Lock 防止并发调用引发状态不一致;
  • 在事件回调中禁止直接调用 stop()(因可能触发主线程阻塞或上下文错误),应通过 Flask 路由异步触发;
  • 若需在设备接入时跳转页面(如 /new_device),应在路由层统一处理,而非在 device_event_monitor 中调用 render_template(该函数依赖 Flask 请求上下文,而 pyudev 回调运行在独立线程中,无上下文)。

✅ 推荐实现代码(含关键注释)

from flask import Flask, render_template_string, request
from flask_socketio import SocketIO
from threading import Lock
import pyudev

class USBMonitor:
    def __init__(self):
        self.context = pyudev.Context()
        self.monitor = pyudev.Monitor.from_netlink(self.context)
        self.monitor.filter_by('usb')
        self.monitor.enable_receiving()
        self.observer = None
        self._lock = Lock()

    def on_event(self, action, device):
        """USB 事件回调 —— 仅做轻量处理,避免阻塞或跨线程操作"""
        if device.subsystem == 'usb' and action == 'add' and device.device_type == 'usb_device':
            # ✅ 安全操作:发送消息到前端(通过 SocketIO)
            device_info = {
                'action': action,
                'vendor': device.get('ID_VENDOR_FROM_DATABASE', 'Unknown'),
                'model': device.get('ID_MODEL_FROM_DATABASE', 'Unknown'),
                'devpath': device.get('DEVPATH', ''),
            }
            sio.emit('usb_event', device_info)

            # ❌ 错误示例(勿在回调中执行):
            # stop()          # 跨线程调用不安全
            # return render_template(...)  # 无 Flask 上下文!

    def start(self):
        with self._lock:
            if self.observer is None:
                self.observer = pyudev.MonitorObserver(self.monitor, self.on_event)
                self.observer.start()
                print("✅ USB monitor started.")

    def stop(self):
        with self._lock:
            if self.observer is not None:
                self.observer.stop()
                self.observer = None
                print("✅ USB monitor stopped.")

# 初始化应用与监控器
app = Flask(__name__)
sio = SocketIO(app, cors_allowed_origins="*")
usb_monitor = USBMonitor()

# 前端页面(含启停按钮与实时事件展示)
@app.route('/')
def index():
    return render_template_string('''
    
    
    <title>USB Monitor Control</title><h2>USB Device Monitor</h2>
        <button id="startBtn">Start Monitoring</button>
        <button id="stopBtn" disabled>Stop Monitoring</button>
        <div id="log" style="margin-top:20px; font-family:monospace; white-space:pre-wrap;"></div>

        <script src="https://cdn.socket.io/4.8.1/socket.io.min.js"></script><script>
            const socket = io();
            const logEl = document.getElementById('log');
            const startBtn = document.getElementById('startBtn');
            const stopBtn = document.getElementById('stopBtn');

            socket.on('usb_event', (data) => {
                const now = new Date().toISOString().slice(11, 19);
                logEl.textContent += `[${now}] ${data.action}: ${data.vendor} ${data.model}\n`;
                logEl.scrollTop = logEl.scrollHeight;
            });

            startBtn.onclick = () => fetch('/start', {method:'POST'})
                .then(() => { startBtn.disabled=true; stopBtn.disabled=false; });
            stopBtn.onclick = () => fetch('/stop', {method:'POST'})
                .then(() => { startBtn.disabled=false; stopBtn.disabled=true; });
        </script>
    ''')

@app.post('/start')
def start_monitor():
    usb_monitor.start()
    return '', 200

@app.post('/stop')
def stop_monitor():
    usb_monitor.stop()
    return '', 200

@app.post('/register_device')
def register_device():
    # ✅ 设备注册逻辑在此统一处理(例如接收表单数据、写入数据库)
    owner_name = request.form.get('owner_name')
    # ... 业务逻辑
    return "Device registered successfully!", 200

if __name__ == '__main__':
    # 启动 Flask + SocketIO(支持热重载)
    sio.run(app, debug=True, host='0.0.0.0', port=5000)

⚠️ 关键注意事项

  • 不要在 pyudev 回调中执行耗时或阻塞操作(如数据库写入、HTTP 请求、render_template),否则会拖慢整个监控线程;
  • 禁用 daemon=True 线程 + while running 循环:这是旧方案的典型反模式,MonitorObserver 本身已足够可靠;
  • 状态同步必须加锁:多线程环境下(如 Flask 多请求 + Observer 线程),对 observer 实例的读写需 threading.Lock 保护;
  • 前端交互推荐使用 Fetch API + Socket.IO:避免页面跳转中断监控,实现实时事件推送;
  • 生产环境需添加异常处理:如 pyudev.UdevError 捕获、USB 权限检查(Linux 下通常需 sudo usermod -aG plugdev $USER)。

通过以上重构,USB 监控的启停将变得原子、可靠且易于维护,彻底解决“调用 stop_usb_monitor() 但进程仍在运行”的核心痛点。

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
flask框架如何搭建
flask框架如何搭建

搭建步骤:1、安装Python和Pip;2、创建虚拟环境;3、安装Flask;4、创建Flask应用;5、运行应用;6、访问应用。想了解更多flask框架的相关内容,可以阅读本专题下面的文章。

2024.06.27

3675

8

Python Flask框架
Python Flask框架

本专题专注于 Python 轻量级 Web 框架 Flask 的学习与实战,内容涵盖路由与视图、模板渲染、表单处理、数据库集成、用户认证以及RESTful API 开发。通过博客系统、任务管理工具与微服务接口等项目实战,帮助学员掌握 Flask 在快速构建小型到中型 Web 应用中的核心技能。

2025.08.25

5604

10

Python Flask Web框架与API开发
Python Flask Web框架与API开发

本专题系统介绍 Python Flask Web框架的基础与进阶应用,包括Flask路由、请求与响应、模板渲染、表单处理、安全性加固、数据库集成(SQLAlchemy)、以及使用Flask构建 RESTful API 服务。通过多个实战项目,帮助学习者掌握使用 Flask 开发高效、可扩展的 Web 应用与 API。

2025.12.15

296

16

pycharm怎么改成中文
pycharm怎么改成中文

PyCharm是一种Python IDE(Integrated Development Environment,集成开发环境),带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具,比如调试、语法高亮、项目管理、代码跳转、智能提示、自动完成、单元测试、版本控制。此外,该IDE提供了一些高级功能,以用于支持Django框架下的专业Web开发。php中文网给大家带来了pycharm相关的教程以及文章,欢迎大家前来学习和阅读。

2023.07.25

2929

3

pycharm安装教程
pycharm安装教程

PyCharm是一款由JetBrains开发的Python集成开发环境(IDE),它提供了许多方便的功能和工具。本专题为大家带来pycharm安装教程,帮助大家解决问题。

2023.08.21

5537

4

如何解决pycharm找不到模块
如何解决pycharm找不到模块

解决pycharm找不到模块的方法:1、检查python解释器;2、安装缺失的模块;3、检查项目结构;4、检查系统路径;5、使用虚拟环境;6、重启PyCharm或电脑。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.12.04

738

5

如何安装pycharm
如何安装pycharm

安装pycharm的步骤:1、访问PyCharm官方网站下载最新版本的PyCharm;2、下载完成后,打开安装文件;3、安装完成后,打开PyCharm;4、在PyCharm的主界面中等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.02.23

854

5

python和pycharm的区别
python和pycharm的区别

Python和PyCharm是两个不同的概念,它们的区别如下:1、Python是一种编程语言,而PyCharm是一款Python集成开发环境;2、Python可以运行在各种不同的开发环境中,而PyCharm是专门为Python开发而设计的IDE等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.02.23

507

5

pycharm环境如何配置
pycharm环境如何配置

配置教程:1、下载并安装PyCharm;2、选择Python解释器;3、配置虚拟环境;4、配置代码风格;5、配置调试器;6、配置版本控制工具;7、配置插件;8、配置Python路径和环境变量;9、配置其他选项。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2024.02.23

522

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.7万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 133.8万人学习