Files
banban/talkingq-url/services/task_manager.py
2026-03-24 15:04:36 +08:00

154 lines
6.0 KiB
Python

import asyncio
import weakref
from typing import Dict, Set, Optional
from utils.logger import session_logger
class TaskManager:
"""管理异步任务,防止内存泄露"""
def __init__(self):
self.device_tasks: Dict[str, Set[asyncio.Task]] = {}
self.session_tasks: Dict[tuple, Set[asyncio.Task]] = {}
self.cleanup_tasks: Set[asyncio.Task] = set()
self.lock = asyncio.Lock()
async def create_task(self, coro, device_id: Optional[str] = None, session_key: Optional[tuple] = None, task_type: str = "general"):
"""创建并跟踪异步任务"""
task = asyncio.create_task(coro)
async with self.lock:
# 按设备ID跟踪
if device_id:
if device_id not in self.device_tasks:
self.device_tasks[device_id] = set()
self.device_tasks[device_id].add(task)
# 按会话跟踪
if session_key:
if session_key not in self.session_tasks:
self.session_tasks[session_key] = set()
self.session_tasks[session_key].add(task)
# 跟踪清理任务
if task_type == "cleanup":
self.cleanup_tasks.add(task)
# 任务完成后自动清理
task.add_done_callback(lambda t: asyncio.create_task(self._cleanup_completed_task(t, device_id, session_key, task_type)))
session_logger.info(
device_id or "system",
session_key[1] if session_key else "task_manager",
f"创建任务: {task_type}, 总任务数: {len(self.cleanup_tasks) if task_type == 'cleanup' else 'tracked'}"
)
return task
async def _cleanup_completed_task(self, task: asyncio.Task, device_id: Optional[str], session_key: Optional[tuple], task_type: str):
"""清理已完成的任务"""
async with self.lock:
if device_id and device_id in self.device_tasks:
self.device_tasks[device_id].discard(task)
if not self.device_tasks[device_id]:
del self.device_tasks[device_id]
if session_key and session_key in self.session_tasks:
self.session_tasks[session_key].discard(task)
if not self.session_tasks[session_key]:
del self.session_tasks[session_key]
if task_type == "cleanup":
self.cleanup_tasks.discard(task)
async def cancel_device_tasks(self, device_id: str):
"""取消设备的所有任务"""
async with self.lock:
if device_id in self.device_tasks:
tasks = list(self.device_tasks[device_id])
session_logger.info(device_id, "task_manager", f"取消设备任务数量: {len(tasks)}")
for task in tasks:
if not task.done():
task.cancel()
# 等待任务取消完成
if tasks:
try:
await asyncio.wait_for(asyncio.gather(*tasks, return_exceptions=True), timeout=5.0)
except asyncio.TimeoutError:
session_logger.warning(device_id, "task_manager", "部分任务取消超时")
del self.device_tasks[device_id]
async def cancel_session_tasks(self, session_key: tuple):
"""取消会话的所有任务"""
async with self.lock:
if session_key in self.session_tasks:
tasks = list(self.session_tasks[session_key])
device_id, session_id = session_key
session_logger.info(device_id, session_id, f"取消会话任务数量: {len(tasks)}")
for task in tasks:
if not task.done():
task.cancel()
# 等待任务取消完成
if tasks:
try:
await asyncio.wait_for(asyncio.gather(*tasks, return_exceptions=True), timeout=3.0)
except asyncio.TimeoutError:
session_logger.warning(device_id, session_id, "部分会话任务取消超时")
del self.session_tasks[session_key]
async def get_task_stats(self) -> dict:
"""获取任务统计信息"""
async with self.lock:
device_count = sum(len(tasks) for tasks in self.device_tasks.values())
session_count = sum(len(tasks) for tasks in self.session_tasks.values())
return {
"device_tasks": device_count,
"session_tasks": session_count,
"cleanup_tasks": len(self.cleanup_tasks),
"total_tracked_devices": len(self.device_tasks),
"total_tracked_sessions": len(self.session_tasks)
}
async def cleanup_all_tasks(self):
"""清理所有任务"""
async with self.lock:
all_tasks = []
# 收集所有任务
for tasks in self.device_tasks.values():
all_tasks.extend(tasks)
for tasks in self.session_tasks.values():
all_tasks.extend(tasks)
all_tasks.extend(self.cleanup_tasks)
session_logger.info("system", "task_manager", f"开始清理所有任务,总数: {len(all_tasks)}")
# 取消所有任务
for task in all_tasks:
if not task.done():
task.cancel()
# 等待任务取消完成
if all_tasks:
try:
await asyncio.wait_for(asyncio.gather(*all_tasks, return_exceptions=True), timeout=10.0)
except asyncio.TimeoutError:
session_logger.warning("system", "task_manager", "部分任务清理超时")
# 清空所有跟踪
self.device_tasks.clear()
self.session_tasks.clear()
self.cleanup_tasks.clear()
session_logger.info("system", "task_manager", "任务管理器清理完成")
# 全局任务管理器实例
task_manager = TaskManager()