Files
deepdata/runtime/deepdata-python/app/module/generation/service/task_executor.py
T

138 lines
4.1 KiB
Python

"""
独立任务执行器 - 真正隔离后台任务与主事件循环
使用线程池执行CPU密集型/阻塞操作,避免阻塞主事件循环
"""
import asyncio
import concurrent.futures
from typing import Any, Callable
from functools import partial
from app.core.logging import get_logger
logger = get_logger(__name__)
# 全局线程池 - 用于执行阻塞操作
# 使用 ProcessPoolExecutor 处理CPU密集型任务,ThreadPoolExecutor 处理I/O密集型任务
_thread_pool: concurrent.futures.ThreadPoolExecutor | None = None
_process_pool: concurrent.futures.ProcessPoolExecutor | None = None
# 任务队列 - 限制并发任务数量
_task_semaphore: asyncio.Semaphore | None = None
def init_executor(
max_workers: int = 10,
max_concurrent_tasks: int = 5,
):
"""
初始化执行器
Args:
max_workers: 线程池最大工作线程数
max_concurrent_tasks: 最大并发任务数(同时执行的数据合成任务数)
"""
global _thread_pool, _process_pool, _task_semaphore
if _thread_pool is None:
_thread_pool = concurrent.futures.ThreadPoolExecutor(
max_workers=max_workers,
thread_name_prefix="generation_task_"
)
logger.info(f"Initialized thread pool with {max_workers} workers")
if _task_semaphore is None:
_task_semaphore = asyncio.Semaphore(max_concurrent_tasks)
logger.info(f"Initialized task semaphore with limit {max_concurrent_tasks}")
def get_thread_pool() -> concurrent.futures.ThreadPoolExecutor:
"""获取线程池,如果未初始化则自动初始化"""
global _thread_pool
if _thread_pool is None:
init_executor()
return _thread_pool
def get_task_semaphore() -> asyncio.Semaphore:
"""获取任务信号量,如果未初始化则自动初始化"""
global _task_semaphore
if _task_semaphore is None:
init_executor()
return _task_semaphore
async def run_in_thread[
T
](func: Callable[..., T], *args, **kwargs) -> T:
"""
在线程池中执行阻塞函数
Args:
func: 要执行的阻塞函数
*args: 位置参数
**kwargs: 关键字参数
Returns:
函数执行结果
"""
pool = get_thread_pool()
loop = asyncio.get_running_loop()
# 包装函数,添加超时和异常处理
def wrapper():
try:
return func(*args, **kwargs)
except Exception as e:
logger.error(f"Error in thread execution: {e}")
raise
try:
# 使用 asyncio.wait_for 添加超时控制
return await asyncio.wait_for(
loop.run_in_executor(pool, wrapper),
timeout=300 # 5分钟超时
)
except asyncio.TimeoutError:
logger.error(f"Thread execution timeout after 300s")
raise
async def execute_generation_task(task_id: str, process_task_func: Callable[..., Any]):
"""
执行数据合成任务,带并发控制和资源隔离
Args:
task_id: 任务ID
process_task_func: 异步任务处理函数,接收task_id作为参数
"""
semaphore = get_task_semaphore()
async with semaphore:
logger.info(f"Starting generation task {task_id} (semaphore acquired)")
try:
# 直接调用异步函数,而不是通过run_in_thread
# 因为process_task_func已经是异步函数
await process_task_func(task_id)
logger.info(f"Generation task {task_id} completed successfully")
except Exception as e:
logger.exception(f"Generation task {task_id} failed: {e}")
raise
def shutdown_executor():
"""关闭执行器,释放资源"""
global _thread_pool, _process_pool, _task_semaphore
if _thread_pool:
_thread_pool.shutdown(wait=True)
_thread_pool = None
logger.info("Thread pool shutdown")
if _process_pool:
_process_pool.shutdown(wait=True)
_process_pool = None
logger.info("Process pool shutdown")
_task_semaphore = None