Files
2026-07-13 12:34:57 +08:00

188 lines
5.6 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8
"""
异步执行工具模块
提供可复用的异步执行方法
"""
import asyncio
import functools
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
from typing import Callable
from loguru import logger as loguru_logger
logger = loguru_logger.bind(name='async_utils')
class AsyncExecutor:
"""异步执行器,提供可复用的异步执行方法"""
def __init__(self, max_workers: int = 4, executor_type: str = 'thread'):
"""
初始化异步执行器
Args:
max_workers: 最大工作线程/进程数
executor_type: 执行器类型,'thread' 或 'process'
"""
self.max_workers = max_workers
self.executor_type = executor_type
self._executor = None
self._loop = None
@property
def executor(self):
"""获取执行器实例"""
if self._executor is None:
if self.executor_type == 'thread':
self._executor = ThreadPoolExecutor(max_workers=self.max_workers)
elif self.executor_type == 'process':
self._executor = ProcessPoolExecutor(max_workers=self.max_workers)
else:
raise ValueError(f"不支持的执行器类型: {self.executor_type}")
return self._executor
@property
def loop(self):
"""获取事件循环"""
if self._loop is None:
try:
self._loop = asyncio.get_event_loop()
except RuntimeError:
self._loop = asyncio.new_event_loop()
asyncio.set_event_loop(self._loop)
return self._loop
async def run_in_background(self, func: Callable, *args, **kwargs) -> asyncio.Task:
"""
在后台异步执行函数
Args:
func: 要执行的函数
*args: 函数参数
**kwargs: 函数关键字参数
Returns:
asyncio.Task: 异步任务对象
"""
task = asyncio.create_task(self._execute_func(func, *args, **kwargs))
return task
async def _execute_func(self, func: Callable, *args, task_name: str = None, **kwargs):
"""执行函数的内部方法"""
try:
# 使用提供的任务名称或函数名
display_name = task_name if task_name else func.__name__
# logger.info(f"开始执行后台任务: {display_name}")
result = await self.loop.run_in_executor(
self.executor,
functools.partial(func, *args, **kwargs)
)
# logger.info(f"后台任务执行完成: {display_name}")
return result
except Exception as e:
display_name = task_name if task_name else func.__name__
# logger.error(f"后台任务执行失败: {display_name}, 错误: {e}", exc_info=True)
raise
def close(self):
"""关闭执行器,释放资源"""
if self._executor:
self._executor.shutdown(wait=True)
self._executor = None
# logger.info("异步执行器已关闭")
# 全局异步执行器实例
_global_executor = None
def get_async_executor(max_workers: int = 4, executor_type: str = 'thread') -> AsyncExecutor:
"""
获取全局异步执行器实例
Args:
max_workers: 最大工作线程/进程数
executor_type: 执行器类型
Returns:
AsyncExecutor: 异步执行器实例
"""
global _global_executor
if _global_executor is None:
_global_executor = AsyncExecutor(max_workers, executor_type)
return _global_executor
def async_run(func: Callable, *args, **kwargs) -> asyncio.Task:
"""
快速启动后台任务的便捷函数
Args:
func: 要执行的函数
*args: 函数参数
**kwargs: 函数关键字参数,支持 task_name 用于日志显示
Returns:
asyncio.Task: 异步任务对象
"""
executor = get_async_executor()
# 从kwargs中提取task_name,如果没有则使用函数名
task_name = kwargs.pop('task_name', None)
display_name = task_name if task_name else func.__name__
# 创建异步任务,确保在后台运行
task = asyncio.create_task(executor._execute_func(func, *args, task_name=display_name, **kwargs))
return task
# 装饰器:将同步函数转换为异步函数
def async_task(func: Callable) -> Callable:
"""
装饰器:将同步函数包装为异步函数
Usage:
@async_task
def my_sync_function():
# 同步代码
pass
# 现在可以异步调用
await my_sync_function()
"""
@functools.wraps(func)
async def wrapper(*args, **kwargs):
executor = get_async_executor()
return await executor._execute_func(func, *args, **kwargs)
return wrapper
# 上下文管理器:自动管理执行器生命周期
class AsyncExecutorContext:
"""异步执行器上下文管理器"""
def __init__(self, max_workers: int = 4, executor_type: str = 'thread'):
self.max_workers = max_workers
self.executor_type = executor_type
self.executor = None
async def __aenter__(self):
self.executor = AsyncExecutor(self.max_workers, self.executor_type)
return self.executor
async def __aexit__(self, exc_type, exc_val, exc_tb):
if self.executor:
self.executor.close()
# 清理函数
def cleanup_async_executor():
"""清理全局异步执行器"""
global _global_executor
if _global_executor:
_global_executor.close()
_global_executor = None