fix: step11 移除 ProcessPoolExecutor 改用顺序生成避免 Windows spawn 死锁

问题: ProcessPoolExecutor 在 Windows spawn 模式下,每个 worker
  需重新导入 __main__ (water_quality_gui_v2.py) 及全部依赖
  (PyQt5, GDAL, rasterio...),启动极慢且极易因环境差异死锁。

修复: 改为顺序 for 循环,每个 CSV 直接在当前 WorkerThread 调用
  _process_one_map。每张图生成前发进度通知,完全透明。

时间预估: 16 块 × ~25s/块 ≈ 6-8 分钟/张,63 张 ≈ 6-8 小时。
  用户可随时看到进度,心跳线程持续保活防止超时误杀。
This commit is contained in:
duxin
2026-07-08 14:18:40 +08:00
parent 9512cab807
commit 3f023cffd4

View File

@ -206,42 +206,32 @@ class Step11MapHandler(BaseStepHandler):
context.notify('step11_map', 'warning',
f'共享上下文预计算失败: {e},回退逐个处理')
# ── 多进程并行GDAL 线程不安全,但进程隔离下安全)──
# ── 顺序生成(避免 Windows spawn 下 ProcessPoolExecutor 死锁)──
# 局部 Kriging 内部已做 16 块顺序分块,每块 ~20-30s
# 每张图约 5-8 分钟。64 张 ≈ 5-8 小时,但进度完全透明可见。
generated: List[str] = []
errors: Dict[str, str] = {}
import multiprocessing
from concurrent.futures import ProcessPoolExecutor, as_completed
context.notify('step11_map', 'info',
f'顺序生成 {total} 张专题图(局部 Kriging 自适应分块)')
# 留出 1-2 个核心保证电脑不卡死
max_workers = max(1, multiprocessing.cpu_count() - 2)
context.notify('step11_map', 'info', f'使用 {max_workers} 个进程并行生成')
for idx, csv_p in enumerate(csv_paths):
percent = int(idx / total * 100)
context.notify('step11_map', 'info',
f'专题图 [{idx+1}/{total}]: {Path(csv_p).name}')
with ProcessPoolExecutor(max_workers=max_workers) as executor:
future_to_csv = {
executor.submit(_process_one_map, csv_p, base_kwargs, output_dir): csv_p
for csv_p in csv_paths
}
done_count = 0
for future in as_completed(future_to_csv):
csv_p = future_to_csv[future]
done_count += 1
try:
result_path, _ = future.result()
generated.append(result_path)
except Exception as e:
errors[csv_p] = str(e)
context.notify('step11_map', 'warning',
f'专题图 FAIL: {Path(csv_p).name}{e}')
global_event_bus.publish('ProgressUpdate', {
'percentage': percent,
'message': f'Step11: {idx+1}/{total} {Path(csv_p).stem}',
})
pct = int(done_count / total * 100)
global_event_bus.publish('ProgressUpdate', {
'percentage': pct,
'message': f'Step11 专题图: {done_count}/{total}',
})
if done_count % max(1, total // 10) == 0 or done_count == total:
context.notify('step11_map', 'info',
f'专题图 [{done_count}/{total}]')
try:
result_path, _ = _process_one_map(csv_p, base_kwargs, output_dir)
generated.append(result_path)
except Exception as e:
errors[csv_p] = str(e)
context.notify('step11_map', 'warning',
f'专题图 FAIL: {Path(csv_p).name}{e}')
step_end_time = time.time()
elapsed = step_end_time - step_start_time