# 批量分析限制说明

## 概述

为了保证系统稳定性和性能，批量分析功能有以下限制：

## 限制参数

### 1. 批量分析数量限制

- **最多支持：10个股票**
- **最少需要：1个股票**

### 2. 并发执行限制

- **最多同时执行：3个分析任务**
- **超过3个任务：自动排队等待**

## 工作原理

### 提交5个股票代码的执行流程

假设用户提交了5个股票代码：`["000001", "600519", "600036", "000002", "600000"]`

```
时间线：
┌─────────────────────────────────────────────────────────────┐
│ 0s: 创建5个任务                                              │
│     ├─ 000001 (任务1) ✅ 创建成功                            │
│     ├─ 600519 (任务2) ✅ 创建成功                            │
│     ├─ 600036 (任务3) ✅ 创建成功                            │
│     ├─ 000002 (任务4) ✅ 创建成功                            │
│     └─ 600000 (任务5) ✅ 创建成功                            │
├─────────────────────────────────────────────────────────────┤
│ 1s: 开始执行（线程池有3个工作线程）                          │
│     ├─ 000001 (任务1) 🚀 开始执行                            │
│     ├─ 600519 (任务2) 🚀 开始执行                            │
│     ├─ 600036 (任务3) 🚀 开始执行                            │
│     ├─ 000002 (任务4) ⏳ 排队等待                            │
│     └─ 600000 (任务5) ⏳ 排队等待                            │
├─────────────────────────────────────────────────────────────┤
│ 5分钟: 任务1完成                                             │
│     ├─ 000001 (任务1) ✅ 完成                                │
│     ├─ 600519 (任务2) 🔄 执行中                              │
│     ├─ 600036 (任务3) 🔄 执行中                              │
│     ├─ 000002 (任务4) 🚀 开始执行（线程空闲）                │
│     └─ 600000 (任务5) ⏳ 排队等待                            │
├─────────────────────────────────────────────────────────────┤
│ 6分钟: 任务2完成                                             │
│     ├─ 000001 (任务1) ✅ 完成                                │
│     ├─ 600519 (任务2) ✅ 完成                                │
│     ├─ 600036 (任务3) 🔄 执行中                              │
│     ├─ 000002 (任务4) 🔄 执行中                              │
│     └─ 600000 (任务5) 🚀 开始执行（线程空闲）                │
├─────────────────────────────────────────────────────────────┤
│ 15分钟: 所有任务完成                                         │
│     ├─ 000001 (任务1) ✅ 完成                                │
│     ├─ 600519 (任务2) ✅ 完成                                │
│     ├─ 600036 (任务3) ✅ 完成                                │
│     ├─ 000002 (任务4) ✅ 完成                                │
│     └─ 600000 (任务5) ✅ 完成                                │
└─────────────────────────────────────────────────────────────┘
```

### 关键点

1. **所有任务都会被创建**：用户可以在任务中心看到所有任务
2. **但只有3个任务同时执行**：受线程池限制
3. **其他任务自动排队**：等待线程空闲后自动开始
4. **总耗时 ≈ 单个任务耗时 × (任务数 / 3)**：例如5个任务，总耗时约为单个任务的1.67倍

## 错误提示

### 超过数量限制

如果提交了超过10个股票代码，会收到以下错误：

```json
{
  "success": false,
  "message": "批量分析最多支持 10 个股票，当前提交了 15 个"
}
```

### 空列表

如果提交了空的股票代码列表，会收到以下错误：

```json
{
  "success": false,
  "message": "股票代码列表不能为空"
}
```

## 性能建议

### 最佳实践

1. **推荐批量大小：3-5个股票**
   - 可以充分利用并发能力
   - 不会等待太久
   - 资源使用合理

2. **避免提交过多任务**
   - 虽然最多支持10个，但建议分批提交
   - 例如：20个股票分成2批，每批10个

3. **根据分析级别调整批量大小**
   - 快速分析（级别1-2）：可以提交8-10个
   - 标准分析（级别3）：建议5-7个
   - 深度分析（级别4-5）：建议3-5个

### 预估时间

| 分析级别 | 单个任务耗时 | 3个任务耗时 | 5个任务耗时 | 10个任务耗时 |
|---------|------------|-----------|-----------|------------|
| 快速    | 2-3分钟    | 2-3分钟   | 4-5分钟   | 7-10分钟   |
| 基础    | 3-5分钟    | 3-5分钟   | 5-8分钟   | 10-17分钟  |
| 标准    | 5-8分钟    | 5-8分钟   | 8-13分钟  | 17-27分钟  |
| 深度    | 8-12分钟   | 8-12分钟  | 13-20分钟 | 27-40分钟  |
| 全面    | 12-20分钟  | 12-20分钟 | 20-33分钟 | 40-67分钟  |

**计算公式**：`总耗时 ≈ 单个任务耗时 × ceil(任务数 / 3)`

## 系统配置

### 调整线程池大小

如果服务器资源充足，可以增加线程池大小：

```python
# app/services/simple_analysis_service.py
self._thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=5)  # 改为5
```

**注意**：
- 增加线程数会增加内存和CPU使用
- 建议根据服务器资源调整
- 推荐值：2-5个工作线程

### 调整批量分析数量限制

如果需要支持更多股票，可以修改限制：

```python
# app/models/analysis.py
symbols: Optional[List[str]] = Field(None, min_items=1, max_items=20, description="股票代码列表（最多20个）")

# app/routers/analysis.py
MAX_BATCH_SIZE = 20  # 改为20
```

**注意**：
- 增加限制会增加系统负载
- 建议同时增加线程池大小
- 推荐值：10-20个股票

## 监控和日志

### 查看并发执行情况

在日志中可以看到：

```
🚀 [并发任务] 开始执行: xxx-xxx-xxx - 000001
🚀 [并发任务] 开始执行: yyy-yyy-yyy - 600519
🚀 [并发任务] 开始执行: zzz-zzz-zzz - 600036
🚀 [线程池] 提交分析任务到共享线程池: aaa-aaa-aaa - 000002
⏳ [线程池] 任务排队等待: aaa-aaa-aaa - 000002
```

### 任务中心显示

- **进行中任务**：显示所有正在执行和排队的任务
- **任务状态**：
  - `running`：正在执行或排队等待
  - `completed`：已完成
  - `failed`：执行失败

## 常见问题

### Q1: 为什么任务中心显示5个"进行中"任务，但实际只有3个在执行？

**A**: 这是正常的。所有任务都会被创建并标记为"进行中"，但受线程池限制，只有3个任务真正在执行，其他任务在排队等待。

### Q2: 如何知道哪些任务在执行，哪些在排队？

**A**: 可以通过日志查看：
- `🚀 [并发任务] 开始执行`：任务开始执行
- `✅ [并发任务] 执行完成`：任务执行完成
- 两者之间的时间差就是任务的执行时间

### Q3: 可以取消排队中的任务吗？

**A**: 目前不支持取消排队中的任务。一旦任务被创建，就会按顺序执行。建议在提交前仔细确认股票代码列表。

### Q4: 如果提交了10个股票，可以再提交10个吗？

**A**: 可以。每次批量分析是独立的，可以同时提交多个批次。但要注意总的并发任务数不要超过线程池限制。

### Q5: 为什么限制为10个股票？

**A**: 主要考虑：
1. **资源限制**：每个分析任务需要大量内存和API调用
2. **用户体验**：等待时间过长会影响用户体验
3. **系统稳定性**：防止过多任务导致系统崩溃

如果需要分析更多股票，建议分批提交。

## 技术细节

### 线程池实现

```python
# 共享线程池
self._thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=3)

# 提交任务到线程池
result = await loop.run_in_executor(
    self._thread_pool,
    self._run_analysis_sync,
    task_id,
    user_id,
    request,
    progress_tracker
)
```

### 并发控制

使用 `asyncio.create_task` 实现真正的并发：

```python
async def run_concurrent_analysis():
    tasks = []
    for symbol in stock_symbols:
        task = asyncio.create_task(run_single_analysis(...))
        tasks.append(task)
    await asyncio.gather(*tasks, return_exceptions=True)

asyncio.create_task(run_concurrent_analysis())
```

### 线程安全

使用 `threading.Lock` 保证线程安全：

```python
# 在 MemoryStateManager 中
self._lock = threading.Lock()

# 更新任务状态时加锁
with self._lock:
    task = self._tasks[task_id]
    task.status = status
    task.progress = progress
```

## 参考资料

- [批量分析问题修复总结](../troubleshooting/batch-analysis-fix-summary.md)
- [并发安全总结](../troubleshooting/concurrent-safety-summary.md)

