- FastAPI backend with build queue, WebSocket logs, task management - Vue 3 frontend with build/config/history views - Xcode project build automation with IPA export - Fix: initialize build_dir before try block to ensure cleanup on early failure
81 lines
2.5 KiB
Python
81 lines
2.5 KiB
Python
"""并行打包队列管理"""
|
||
import asyncio
|
||
from typing import Dict, Callable, Optional
|
||
from datetime import datetime
|
||
|
||
from .log_streamer import log_streamer
|
||
|
||
|
||
class BuildQueue:
|
||
"""打包任务队列"""
|
||
|
||
def __init__(self, max_concurrent: int = 2):
|
||
self._max_concurrent = max_concurrent
|
||
self._semaphore = asyncio.Semaphore(max_concurrent)
|
||
self._queue: asyncio.Queue = asyncio.Queue()
|
||
self._running_tasks: Dict[str, asyncio.Task] = {}
|
||
self._workers: list = []
|
||
self._started = False
|
||
|
||
async def start(self):
|
||
"""启动工作线程"""
|
||
if self._started:
|
||
return
|
||
self._started = True
|
||
for i in range(self._max_concurrent):
|
||
worker = asyncio.create_task(self._worker(f"worker-{i}"))
|
||
self._workers.append(worker)
|
||
|
||
async def _worker(self, name: str):
|
||
"""工作协程"""
|
||
while True:
|
||
task_id, build_func = await self._queue.get()
|
||
try:
|
||
await self._semaphore.acquire()
|
||
try:
|
||
self._running_tasks[task_id] = asyncio.current_task()
|
||
await build_func(task_id)
|
||
finally:
|
||
self._running_tasks.pop(task_id, None)
|
||
self._semaphore.release()
|
||
except Exception as e:
|
||
await log_streamer.emit_error(task_id, f"任务异常: {str(e)}")
|
||
finally:
|
||
self._queue.task_done()
|
||
|
||
async def submit(self, task_id: str, build_func: Optional[Callable] = None):
|
||
"""提交打包任务"""
|
||
if not self._started:
|
||
await self.start()
|
||
|
||
if build_func is None:
|
||
from .build_service import run_build_task
|
||
build_func = run_build_task
|
||
|
||
await self._queue.put((task_id, build_func))
|
||
await log_streamer.emit(task_id, "任务已加入队列,等待执行...")
|
||
|
||
def cancel(self, task_id: str):
|
||
"""取消任务"""
|
||
task = self._running_tasks.get(task_id)
|
||
if task and not task.done():
|
||
task.cancel()
|
||
|
||
def update_max_concurrent(self, new_max: int):
|
||
"""更新最大并发数"""
|
||
self._max_concurrent = new_max
|
||
# 注意:运行时修改需要重建 semaphore,这里简化处理
|
||
# 实际使用时建议重启服务生效
|
||
|
||
@property
|
||
def queue_size(self) -> int:
|
||
return self._queue.qsize()
|
||
|
||
@property
|
||
def running_count(self) -> int:
|
||
return len(self._running_tasks)
|
||
|
||
|
||
# 全局单例
|
||
build_queue = BuildQueue()
|