修复启动卡顿:线程分批启动避免GIL竞争+病句过滤增强

This commit is contained in:
youpaishidifu
2026-08-08 10:33:48 +08:00
parent 7fc737d80f
commit 2a2161820f
+44 -10
View File
@@ -342,6 +342,8 @@ class MessageMonitor:
def __init__(self, on_message: Optional[Callable[[str, str], None]] = None):
self._running = False
self._thread: Optional[threading.Thread] = None
self._qq_thread: Optional[threading.Thread] = None
self._emergency_thread: Optional[threading.Thread] = None
self._poll_interval = 2.0
self._on_message = on_message
self._wechat_enabled = True
@@ -526,20 +528,34 @@ class MessageMonitor:
self._ocr_engine_wins = {"windows": 0, "easyocr": 0}
# OCR 截屏定时清理:启动后 30~60 秒随机触发第一次
self._next_ocr_clean_at = time.time() + random.uniform(30, 60)
# EasyOCR 已在子进程隔离(easyocr_worker.py),不需在主线程预加载
# ⭐ 预热子进程:后台启动 + 60s 初始化握手,主循环不阻塞
# 否则首次 OCR 时 10s 请求超时会把初始化中的进程杀掉 → 永远超时
if _HAS_EASYOCR and not self._easyocr_broken:
_warm = threading.Thread(target=self._ensure_easyocr_subprocess, daemon=True, name="easyocr-warmup")
_warm.start()
# ⭐ 启动线程错峰:5个线程同时拉起导致 GIL 竞争,GUI 冻结 6-7 秒。
# 方案:按优先级分批启动,批间 sleep(0.5) 让 GUI 主线程有机会刷新。
# 第1批(立即):微信主轮询(核心功能) + 急停热键(安全)
# 第2批(0.5s):QQ轮询(独立平台,不依赖微信)
# 第3批(1.0s):EasyOCR子进程预热(重操作,延迟避免挤占GUI)
# 第1批:核心线程
self._thread = threading.Thread(target=self._run_loop, daemon=True, name="monitor")
self._thread.start()
# QQ 独立轮询线程,不阻塞微信主循环(WeChat EasyOCR ~15-20s 每轮)
self._qq_thread = threading.Thread(target=self._qq_poll_loop, daemon=True, name="monitor-qq")
self._qq_thread.start()
# 双击 ESC 急停检测线程
self._emergency_thread = threading.Thread(target=self._esc_check_loop, daemon=True, name="monitor-esc")
self._emergency_thread.start()
# 第2批:QQ轮询(延迟0.5s,让GUI有机会刷新 + 避免同时初始化Windows消息队列)
def _delayed_qq_start():
time.sleep(0.5)
if self._running:
self._qq_thread = threading.Thread(target=self._qq_poll_loop, daemon=True, name="monitor-qq")
self._qq_thread.start()
threading.Thread(target=_delayed_qq_start, daemon=True, name="qq-delay").start()
# 第3批:EasyOCR预热(延迟1s,最重的操作放最后)
if _HAS_EASYOCR and not self._easyocr_broken:
def _delayed_easyocr_start():
time.sleep(1.0)
if self._running:
self._ensure_easyocr_subprocess()
threading.Thread(target=_delayed_easyocr_start, daemon=True, name="easyocr-delay").start()
logger.info("消息监控已启动")
def stop(self):
@@ -4858,6 +4874,24 @@ class MessageMonitor:
text):
logger.debug(f"[ocr] 过滤器2结果: 检测到Windows系统进程名,返回True")
return True
# ⭐ 病句过滤:语法不完整片段(缺主语/动词/括号不匹配)
# 如"不得进行任何形式,[" 缺少主语,不应入库
if re.match(r'^(?:不得|禁止|允许|应该|必须|可以|需要|支持|提供|进行|进行任何形式|不得进行)', text):
# 检查是否有主语(我/你/他/我们/你们/他们/玩家/用户/管理员等)
subjects = ('我', '你', '他', '她', '它', '我们', '你们', '他们', '她们', '玩家', '用户', '管理员', '服务端', '客户端')
if not any(s in text for s in subjects):
logger.debug(f"[ocr] 过滤器2结果: 检测到病句(缺主语),返回True")
return True
# 以逗号/顿号结尾的短片段(可能截断)
if len(text) < 10 and text.endswith((',', '、', ',', '.')):
logger.debug(f"[ocr] 过滤器2结果: 检测到短截断片段,返回True")
return True
# 括号不匹配(可能OCR截断)
if text.count('[') != text.count(']') or text.count('(') != text.count(')') or text.count('(') != text.count(')'):
# 仅当文本较短时才判定为病句(避免误杀正常聊天)
if len(text) < 30:
logger.debug(f"[ocr] 过滤器2结果: 检测到括号不匹配,返回True")
return True
logger.debug(f"[ocr] 过滤器2结果: 未检测到噪声特征,返回False")
return False