本文介绍 core 模块的架构设计。core 模块包含与 UI 无关的业务逻辑,可独立运行和测试。
| 模块 | 职责 | 特点 |
|---|---|---|
contact_parser |
将单行文本解析为联系人 | 纯函数,无状态,无 I/O |
vcf_generator |
编排生成管线,管理线程和 I/O | 集成点,协调所有模块 |
phone_detector_loader |
从静态配置加载号码检测器 | 无状态加载,返回字典 |
依赖方向单向:vcf_generator 是唯一对外入口,其余模块互不耦合,便于单独测试:
graph TD
Generator["vcf_generator"] --> Parser["contact_parser"]
Generator --> Models["models"]
Generator --> Queue["utils/deque_queue"]
Parser --> Models
Loader["phone_detector_loader"] --> Configs["configs/phone_detectors"]
Loader --> Models
VCFGeneratorTask 采用两阶段管线,让解析与写入并发执行,并用固定容量队列约束内存:
graph LR
Input["输入文本"] --> W1["Worker 1<br/>解析 + 序列化"]
W1 -->|逐行推送| Queue["DequeQueue<br/>容量 10"]
W1 -->|None 哨兵| Queue
Queue --> W2["Worker 2<br/>写入文件"]
W2 --> Output["输出文件"]
为什么用两阶段管线:
- 资源利用:解析是 CPU 密集型(正则匹配),写入是 I/O 密集型(磁盘),管线让两者并发运行。
- 内存效率:有界队列防止一次性加载全部输出。即使处理 10 万条联系人,内存中也只缓冲少量 vCard。
用户点击取消时,UI 调用 stop():
- 设置
__stopping标志,解析循环在下次迭代时检查并退出。 - 调用
queue.shutdown(),写入端的queue.get()抛出ShutDownError,立即退出。 - 队列中剩余项被丢弃,不等待写入完成(快速响应取消)。
取消是幂等的——多次调用 stop() 不会产生副作用。
- 共享状态:
_GenerationProgress中的计数器由单个RLock保护所有写操作。 - 进度读取:通知线程在无锁状态下读取快照,容忍短暂不一致,避免锁争用。
- 事件驱动:进度变化时通过
_progress_event唤醒通知线程;写入线程完成时设置__all_done = True并触发事件,确保通知线程可退出。
VCFGeneratorTask 通过回调向 UI 层报告状态,自身不依赖 UI:
progress_listener(processed: int, total: int, determinate: bool)— 进度变化时调用result_listener(result: GenerationResult)— 完成时调用
这种控制反转使得核心逻辑可以在无 UI 环境下独立运行和测试。