feat(stream): 添加流循环启动锁以防止并发启动同一流的多个循环任务

This commit is contained in:
Windpicker-owo
2025-11-10 14:23:10 +08:00
parent b427960441
commit e5c5e5c036
2 changed files with 193 additions and 154 deletions

View File

@@ -48,6 +48,9 @@ class StreamLoopManager:
# 每个流的上一次间隔值(用于日志去重)
self._last_intervals: dict[str, float] = {}
# 流循环启动锁:防止并发启动同一个流的多个循环任务
self._stream_start_locks: dict[str, asyncio.Lock] = {}
logger.info(f"流循环管理器初始化完成 (最大并发流数: {self.max_concurrent_streams})")
async def start(self) -> None:
@@ -105,6 +108,14 @@ class StreamLoopManager:
Returns:
bool: 是否成功启动
"""
# 获取或创建该流的启动锁
if stream_id not in self._stream_start_locks:
self._stream_start_locks[stream_id] = asyncio.Lock()
lock = self._stream_start_locks[stream_id]
# 使用锁防止并发启动同一个流的多个循环任务
async with lock:
# 获取流上下文
context = await self._get_stream_context(stream_id)
if not context:

View File

@@ -3,6 +3,7 @@
当定时任务触发时负责搜集信息、调用LLM决策、并根据决策生成回复
"""
import asyncio
from datetime import datetime
from typing import Any, Literal
@@ -484,6 +485,9 @@ _planner = ProactiveThinkingPlanner()
# 统计数据
_statistics: dict[str, dict[str, Any]] = {}
# 全局执行锁字典:防止同一聊天流的主动思考被并发执行
_execution_locks: dict[str, asyncio.Lock] = {}
def _update_statistics(stream_id: str, action: str):
"""更新统计数据
@@ -538,10 +542,34 @@ async def execute_proactive_thinking(stream_id: str):
logger.debug(f"主动思考功能已关闭,跳过执行 {stream_id}")
return
# 获取或创建该聊天流的执行锁
if stream_id not in _execution_locks:
_execution_locks[stream_id] = asyncio.Lock()
lock = _execution_locks[stream_id]
# 尝试获取锁,如果已被占用则跳过本次执行(防止重复)
if lock.locked():
logger.warning(f"⚠️ 主动思考跳过:聊天流 {stream_id} 已有正在执行的主动思考任务")
return
async with lock:
logger.debug(f"🤔 开始主动思考 {stream_id}")
try:
# 0. 前置检查
# 0.0 检查聊天流是否正在处理消息(双重保护)
try:
from src.chat.message_receive.chat_stream import get_chat_manager
chat_manager = get_chat_manager()
chat_stream = await chat_manager.get_stream(stream_id)
if chat_stream and chat_stream.context_manager.context.is_chatter_processing:
logger.warning(f"⚠️ 主动思考跳过:聊天流 {stream_id} 的 chatter 正在处理消息")
return
except Exception as e:
logger.warning(f"检查 chatter 处理状态时出错: {e},继续执行")
# 0.1 检查白名单/黑名单
# 从 stream_id 获取 stream_config 字符串进行验证
try: