diff --git a/gui.py b/gui.py index 2fb454b..80ba70d 100644 --- a/gui.py +++ b/gui.py @@ -123,8 +123,44 @@ class OCREngineManager: self._timer_thread = None self._timer_stop = threading.Event() + @staticmethod + def _probe_cuda_safety(timeout: int = 30) -> bool: + """子进程 CUDA 安全探针:在子进程里做 CUDA 显存分配。 + 返回 True = CUDA 可用且安全,False = 不可用或崩溃。 + 崩溃只杀子进程,不影响主进程。""" + import subprocess, sys + _probe_code = ( + "import torch; " + "assert torch.cuda.is_available(), 'CUDA not available'; " + "t = torch.zeros(1, device='cuda'); " + "del t; " + "torch.cuda.empty_cache(); " + "print('CUDA_OK')" + ) + try: + _py = sys.executable + _r = subprocess.run( + [_py, "-c", _probe_code], + capture_output=True, text=True, timeout=timeout, + creationflags=getattr(subprocess, 'CREATE_NO_WINDOW', 0) + ) + ok = _r.returncode == 0 and "CUDA_OK" in _r.stdout + if not ok: + # 子进程崩溃或超时 + _stderr_snip = (_r.stderr or "")[:200] + logging.getLogger(__name__).warning( + f"[gui] CUDA 子进程探针失败(rc={_r.returncode}): {_stderr_snip}" + ) + return ok + except subprocess.TimeoutExpired: + logging.getLogger(__name__).warning("[gui] CUDA 子进程探针超时") + return False + except Exception as e: + logging.getLogger(__name__).warning(f"[gui] CUDA 子进程探针异常: {e}") + return False + def _check_memory_for_gpu(self) -> bool: - """检查系统内存,决定是否可用GPU模式。与monitor.py同逻辑。""" + """检查系统内存 + CUDA 安全性,决定是否可用GPU模式。""" try: import torch if not torch.cuda.is_available(): @@ -167,6 +203,16 @@ class OCREngineManager: self._log(f" ⚠ 系统提交量过高({commit_pct:.0f}% > 95%),降级CPU模式") return False + # ⭐ Task #1601: CUDA 子进程安全探针 + # torch.cuda.is_available() 通过不代表 CUDA 安全—— + # 显存分配/access violation 在 native 层崩溃,Python try/except 捕获不到。 + # 子进程探针做真实 CUDA tensor 分配,崩溃只杀子进程。 + self._log(" 🔍 运行 CUDA 安全探针...") + if not self._probe_cuda_safety(timeout=30): + self._log(" ⚠ CUDA 探针失败(驱动/显存异常),降级CPU模式") + return False + self._log(" ✅ CUDA 探针通过") + self._log(f" ✅ 内存充足,启用GPU模式") return True @@ -186,6 +232,9 @@ class OCREngineManager: self._log("🔧 初始化OCR引擎...") # 初始化EasyOCR(根据内存条件选择GPU/CPU) + # ⭐ Task #1601: GPU 探针已拦截 CUDA 异常,此处用 try/except 兜底 + # access violation (native crash) 无法被 Python 捕获, + # 但探针通过后此概率极低;CPU 模式不受 native crash 影响。 use_gpu = False try: import easyocr @@ -196,7 +245,7 @@ class OCREngineManager: self._easyocr_reader = easyocr.Reader(['ch_sim', 'en'], gpu=use_gpu, verbose=False) self._log(f" ✅ EasyOCR就绪({mode_str}模式)") except Exception as e: - # GPU失败 → 降级CPU重试 + # GPU失败 → 降级CPU重试(注意:access violation 不可被 except 捕获) if use_gpu: self._log(f" ⚠ EasyOCR GPU初始化失败: {e},降级CPU重试...") try: