讲师中心 微信公众号
AI工具推荐 视频效率加速

如何在 FastAPI WebSocket 服务中安全并发运行两个阻塞函数

阿芳同学_3606

阿芳同学_3606

发布时间:2026-02-12 23:27:43

|

416人浏览过

|

来源于php中文网

原创

如何在 FastAPI WebSocket 服务中安全并发运行两个阻塞函数

本文介绍如何使用多线程(`threading`)在 fastapi websocket 应用中长期、独立、可控制地并行执行两个 i/o 密集型阻塞函数(如磁盘读写),避免阻塞事件循环,同时支持客户端指令动态启停。

在构建实时流式服务(如语音转录、日志监控、传感器数据采集)时,常需一对协同工作的阻塞函数:一个持续写入磁盘(foo),另一个轮询读取并转发结果(bar)。由于它们天然依赖文件系统 I/O,强行改造成 async 不仅增加复杂度(需处理异步文件操作或内存共享),还可能引入资源竞争与取消不可靠等问题。此时,多线程是更简洁、可控且符合实际需求的解决方案。

✅ 正确做法:用 threading.Thread + threading.Event 实现可控并发

核心思路是:

WebSocket 8.18.2
WebSocket 8.18.2

WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。

下载
  • 将 foo 和 bar 分别封装为独立线程;
  • 使用 threading.Event 作为线程间通信的“停止信号”;
  • 在 WebSocket 连接生命周期内启动/停止线程,并确保资源清理。

以下是一个生产就绪的示例实现(已适配 FastAPI WebSocket 场景):

import threading
import time
import json
from fastapi import WebSocket, WebSocketDisconnect
from typing import Dict, Optional

# 全局线程管理:按 client_id 存储运行中的线程与控制事件
ACTIVE_THREADS: Dict[str, Dict[str, any]] = {}

def foo(url: str, client_id: str, stop_event: threading.Event):
    """模拟持续写入磁盘的任务(阻塞式)"""
    file_path = f"/tmp/{client_id}_output.txt"
    counter = 0
    while not stop_event.is_set():
        try:
            # ✅ 模拟耗时 I/O:写入时间戳和计数器
            with open(file_path, "a") as f:
                f.write(f"[{time.time():.3f}] data_{counter}\n")
            counter += 1
            time.sleep(2)  # 控制写入频率,避免过载
        except Exception as e:
            print(f"[foo] Error writing for {client_id}: {e}")
            break

def bar(client_id: str, stop_event: threading.Event, websocket: WebSocket):
    """模拟持续读取磁盘并推送至 WebSocket 的任务(阻塞式)"""
    file_path = f"/tmp/{client_id}_output.txt"
    last_size = 0
    while not stop_event.is_set():
        try:
            # ✅ 安全读取:只读新增内容(类似 tail -f)
            try:
                with open(file_path, "r") as f:
                    f.seek(0, 2)  # 移动到文件末尾
                    size = f.tell()
                    if size > last_size:
                        f.seek(last_size)
                        new_content = f.read(size - last_size).strip()
                        last_size = size
                        if new_content:
                            # ✅ 同步调用 await —— 必须在主线程中执行!
                            # 所以 bar 不直接 send,而是通过队列或回调通知主线程
                            # 这里简化:将数据暂存,由主线程轮询发送(见下文)
                            pass
            except FileNotFoundError:
                pass  # 文件尚未创建,继续等待
            time.sleep(0.5)  # 轮询间隔,避免 CPU 空转
        except Exception as e:
            print(f"[bar] Error reading for {client_id}: {e}")
            break

# ⚠️ 关键注意:WebSocket.send_text() 是 async 函数,不能在线程中直接 await
# 因此 bar 线程不应直接调用 websocket.send_text() —— 这会引发 RuntimeError
# 推荐方案:使用 queue.Queue 或 asyncio.Queue 在线程与事件循环间安全传递数据

? 常见错误与规避策略

问题 原因 解决方案
RuntimeError: await outside async function 在 bar 线程中直接 await websocket.send_text(...) ✅ 改用 asyncio.Queue:bar 线程 put_nowait(data),主线程 await queue.get() 后 send_text
线程无法被及时终止 stop_event 检查不频繁或被阻塞 I/O 卡住 ✅ 在 foo/bar 中加入 time.sleep() 或 event.wait(timeout),确保定期响应中断
文件读写冲突 多线程同时访问同一文件 ✅ 使用 os.path.getmtime() + seek() 增量读取;或采用临时文件 + 原子重命名(如 write → write.tmp → rename)
线程泄漏 WebSocket 断开后未显式 join() 或清理 ✅ 在 finally 块中调用 thread.join(timeout=2) 并删除 ACTIVE_THREADS[client_id]

✅ 推荐增强结构(含安全通信)

import asyncio
import queue
from asyncio import Queue

# 主线程中初始化(在 websocket_endpoint 内):
data_queue: Queue[str] = Queue()

# bar 线程中(改为):
def bar(client_id: str, stop_event: threading.Event, data_queue: Queue):
    file_path = f"/tmp/{client_id}_output.txt"
    last_size = 0
    while not stop_event.is_set():
        try:
            with open(file_path, "r") as f:
                f.seek(0, 2)
                size = f.tell()
                if size > last_size:
                    f.seek(last_size)
                    new_lines = f.read(size - last_size).strip().splitlines()
                    for line in new_lines:
                        if line.strip():
                            asyncio.create_task(data_queue.put(line))  # 安全投递
                    last_size = size
        except Exception as e:
            print(f"[bar] Read error: {e}")
        time.sleep(0.3)

# 主 WebSocket 循环中(新增消费逻辑):
async def consume_queue():
    while not websocket.closed and not stop_event.is_set():
        try:
            data = await asyncio.wait_for(data_queue.get(), timeout=1.0)
            await websocket.send_text(json.dumps({"type": "data", "payload": data}))
        except asyncio.TimeoutError:
            continue
        except Exception as e:
            print(f"[queue consumer] Send error: {e}")
            break

# 启动消费者任务(非阻塞):
consumer_task = asyncio.create_task(consume_queue())

# 停止时:
stop_event.set()
foo_thread.join(timeout=2)
bar_thread.join(timeout=2)
consumer_task.cancel()
await asyncio.gather(consumer_task, return_exceptions=True)

✅ 总结

  • 不要强行 async 化磁盘 I/O 密集型逻辑:threading 更自然、更稳定、更易调试;
  • 必须用 threading.Event 替代全局标志位:确保线程能及时、安全退出;
  • 禁止在线程中调用 await:所有异步操作(如 websocket.send_text)必须回到事件循环主线程执行;
  • 优先选用 asyncio.Queue 跨线程通信:比 threading.Queue 更契合 async 生态,且无死锁风险;
  • 始终 join() 线程并设超时:防止连接异常断开导致后台线程残留。

通过以上设计,你既能保留原有阻塞逻辑的清晰性与可靠性,又能实现毫秒级响应的 WebSocket 实时推送,真正兼顾开发效率与生产健壮性。

热门AI工具

更多
WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

小橡皮AI

一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

VibeKnow
VibeKnow Hot

一款AI视频创作工具,主要用于全球首个AI知识视频创作平台,文档、文章、网页,一键生成视频,适合需要提升相关任务效率的用户。

相关专题

更多
Python FastAPI异步API开发_Python怎么用FastAPI构建异步API
Python FastAPI异步API开发_Python怎么用FastAPI构建异步API

Python FastAPI 异步开发利用 async/await 关键字,通过定义异步视图函数、使用异步数据库库 (如 databases)、异步 HTTP 客户端 (如 httpx),并结合后台任务队列(如 Celery)和异步依赖项,实现高效的 I/O 密集型 API,显著提升吞吐量和响应速度,尤其适用于处理数据库查询、网络请求等耗时操作,无需阻塞主线程。

119

2025.12.22

Python 微服务架构与 FastAPI 框架
Python 微服务架构与 FastAPI 框架

本专题系统讲解 Python 微服务架构设计与 FastAPI 框架应用,涵盖 FastAPI 的快速开发、路由与依赖注入、数据模型验证、API 文档自动生成、OAuth2 与 JWT 身份验证、异步支持、部署与扩展等。通过实际案例,帮助学习者掌握 使用 FastAPI 构建高效、可扩展的微服务应用,提高服务响应速度与系统可维护性。

554

2026.02.06

Python Web框架FastAPI 全栈开发教程合集
Python Web框架FastAPI 全栈开发教程合集

以 FastAPI 为核心,讲解现代 Python Web API 的高效开发方式,涵盖路由定义与路径参数/查询参数/请求体绑定、Pydantic 模型的数据校验与序列化、依赖注入(Depends)系统的分层设计、中间件与 CORS 配置、OAuth2 + JWT 认证流程、后台任务(BackgroundTasks)、WebSocket 实时通信、SQLAlchemy 异步 ORM 集成、自动生成 OpenAPI/Swagger 交互文

656

2026.05.09

Python FastAPI异步微服务与高性能接口设计
Python FastAPI异步微服务与高性能接口设计

本专题聚焦 Python FastAPI 框架在高性能接口与微服务开发中的应用,讲解异步请求处理、依赖注入机制、路由设计、数据库异步操作以及接口性能优化策略。结合实际项目案例,帮助开发者构建高并发、低延迟的现代化后端服务架构。

559

2026.06.16

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

4418

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

1229

2025.12.24

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

423

2026.01.21

C++多线程相关合集
C++多线程相关合集

本专题整合了C++多线程相关教程,阅读专题下面的的文章了解更多详细内容。

579

2026.01.21

Vite 项目如何打包和部署
Vite 项目如何打包和部署

Vite项目打包部署专题汇集生产构建、产物预览、输出目录、资源路径、Nginx与GitHub Pages部署及常见白屏和路由404问题的已审核文章,帮助读者完成上线流程。

0

2026.10.28

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
WebSocket手册
WebSocket手册

共0课时 | 0人学习

Webman中文手册
Webman中文手册

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn