修复启动卡顿:线程分批启动避免GIL竞争+病句过滤增强
This commit is contained in:
+44
-10
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user