From 3f023cffd4ea759244b49f75ba80d17a3506ae8f Mon Sep 17 00:00:00 2001 From: duxin Date: Wed, 8 Jul 2026 14:18:40 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20step11=20=E7=A7=BB=E9=99=A4=20ProcessPoo?= =?UTF-8?q?lExecutor=20=E6=94=B9=E7=94=A8=E9=A1=BA=E5=BA=8F=E7=94=9F?= =?UTF-8?q?=E6=88=90=E9=81=BF=E5=85=8D=20Windows=20spawn=20=E6=AD=BB?= =?UTF-8?q?=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: 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 小时。 用户可随时看到进度,心跳线程持续保活防止超时误杀。 --- src/core/handlers/step11_map_handler.py | 50 ++++++++++--------------- 1 file changed, 20 insertions(+), 30 deletions(-) diff --git a/src/core/handlers/step11_map_handler.py b/src/core/handlers/step11_map_handler.py index 43a1d54..7d90ae9 100644 --- a/src/core/handlers/step11_map_handler.py +++ b/src/core/handlers/step11_map_handler.py @@ -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