Aspen Plus V14 COM 并发与退出故障处理记录
本文记录 Aspen Plus V14 在 Windows COM 自动化中的并发归档故障和退出故障,以及当前项目采用的处理方案。
自动化程序由多个 PowerShell STA worker 组成。每个 worker 使用独立的 Aspen COM 对象、BKP 副本、工作目录和临时目录。测试表明,Run2 可以并行执行,故障主要发生在归档写入和进程退出阶段。
Run2 保持并行。COM 创建和归档加载通过启动互斥锁串行执行,用于确定进程所有权。SaveAs2 → Reconcile → SaveAs2 通过归档互斥锁整体串行执行。退出时只清理当前 worker 认领的进程。
运行环境与故障签名
| 项目 | 内容 |
|---|---|
| Aspen 版本 | Aspen Plus V14 |
| 自动化方式 | PowerShell STA + Apwn.Document COM 对象 |
| 并发方式 | 多个独立 worker,每个 worker 对应独立 Aspen 实例 |
| 故障进程 | apmain.exe |
| 故障模块 | STRGXI2.dll |
| 模块版本 | 2018.11.1.508 |
| Windows 异常代码 | 0xc0000409 |
| COM 调用方错误 | 0x800706BE |
| Windows 事件 | Application Error,Event ID 1000 |
0xc0000409 对应 STATUS_FAIL_FAST_EXCEPTION。该异常会绕过常规异常处理并终止进程,但仅凭异常代码无法确定 Aspen 内部的具体缺陷,也不能据此判定流程计算结果错误。Microsoft 对 C0000409 的说明
故障一:多个进程同时写入归档
并发评价时,各 worker 的 Run2 可以正常完成。多个 worker 同时调用 SaveAs2 时,STRGXI2.dll 可能异常退出,COM 调用方随后收到 0x800706BE。
典型时序如下:
1 | Worker 1 Run2 ─────────────────────────────── 完成 |
每个 worker 使用独立 BKP 文件不能消除该故障。冲突发生在 Aspen V14 的归档层,而不是文件路径重用。
故障二:COM 退出路径异常
归档并发故障之外,当前环境还存在单实例退出异常。Quit、CloseDocument 和最终释放 COM 引用均可能触发 apmain.exe 异常退出。
关闭路径测试结果如下:
| 测试路径 | 是否保存 | 退出方式 | 结果 |
|---|---|---|---|
run_only |
否 | Run2 后调用 Quit |
可出现 Event 1000 |
release_only |
否 | 仅释放 COM 并执行 GC | 可出现相同故障签名 |
CloseDocument |
否 | 仅关闭文档 | 可能挂起,随后引擎退出 |
| 受控清理 | 结果已验收 | 只终止 worker 认领的 PID | 本轮无残留进程 |
不执行 Save、不执行 Reconcile、不开启并发时仍能复现退出异常。因此,退出故障与多进程归档冲突是两个独立问题。
并发边界
一次候选评价按以下阶段执行:
1 | [进程快照 → 创建 COM → 加载 BKP → 进程快照] → [Run2 → 读取并验收结果] |
| 阶段 | 执行方式 | 保护措施 |
|---|---|---|
| COM 创建、BKP 加载、进程认领 | 串行 | 启动互斥锁 |
参数写入、Run2、结果读取 |
并行 | 每个 worker 使用独立 STA、COM 对象和目录 |
SaveAs2 → Reconcile → SaveAs2 |
串行 | 归档互斥锁覆盖完整序列 |
| 退出与异常清理 | 各 worker 独立执行 | 只清理当前 worker 认领的 PID |
这里使用的是互斥锁,不是死锁。互斥锁用于限制临界区只能由一个 worker 进入;死锁是多个执行单元互相等待且无法继续运行的故障状态。两把互斥锁均设置等待超时,并在 finally 中释放。
启动互斥锁
通过 DCOM 创建的 Aspen 进程不一定表现为当前 pwsh 的普通子进程。程序不能只根据父子进程关系判断 Aspen 实例属于哪个 worker。
启动互斥锁覆盖以下操作:
- 获取启动前 Aspen 进程快照;
- 创建
Apwn.DocumentCOM 对象; - 调用
InitFromArchive2加载 worker 的 BKP 副本; - 获取启动后进程快照;
- 根据两次快照认领新增 PID。
实现结构如下。代码经过简化,不包含项目路径和业务参数。
1 | $locked = $startupMutex.WaitOne($startupTimeout) |
完成进程认领后立即释放启动锁。后续 Run2 不在该锁内执行。
归档互斥锁
通过验收的候选需要保存两份归档:
RESULT保存当前计算结果;INITIALIZED保存 Reconcile 后可继续使用的初始化状态。
归档互斥锁覆盖两次保存及其中间的 Reconcile:
1 | $locked = $archiveMutex.WaitOne($archiveTimeout) |
归档锁不能在第一次 SaveAs2 后释放。另一个 worker 如果在 Reconcile 与第二次保存之间进入 SaveAs2,两个进程仍会同时访问归档层。三项操作必须作为一个完整临界区执行。
该互斥锁只限制归档操作,不限制参数写入、Run2 和结果读取。
退出与进程清理
生产配置在结果验收和归档完成后,终止当前 worker 在启动阶段认领的 Aspen 进程。程序不会根据进程名终止全部 Aspen 进程。
简化后的清理逻辑如下:
1 | foreach ($processId in $ownedPids) { |
强制终止只用于已确认存在退出缺陷的当前环境,并且必须在结果持久化之后执行。TerminateProcess 会无条件终止进程,DLL 维护的状态可能无法正常释放,因此不能用于未完成保存的 worker,也不能用于未确认所有权的进程。TerminateProcess 文档
COM 线程模型
每个 worker 使用独立的 pwsh -Sta 进程,并在同一 STA 线程中创建和使用 Aspen COM 对象。COM 接口不会在线程之间直接共享。
该结构符合 COM single-threaded apartment 的基本约束。并发来自多个相互隔离的 STA worker,而不是多个线程共同访问一个 COM 对象。Microsoft 关于 Single-Threaded Apartments 的说明
结果验收
退出阶段异常不能直接用于判断计算结果是否有效。结果是否保留取决于故障发生阶段和验收状态。
当前项目使用以下验收条件:
Run2正常结束,计算和结果读取阶段没有 RPC、RFC 或 COM 异常;- 关键模块和循环达到收敛状态;
- 目标函数、产品指标和质量衡算满足设定容差;
- 结果文件已完整写入,文件大小和哈希检查通过;
- 源 BKP 在运行前后保持不变;
- 最终候选在新的 Aspen 进程中进行两次独立 fresh 验证。
计算、结果读取或保存过程中出现 RPC、RFC 或 COM 异常时,当前结果和 worker 均作废。仅在全部验收完成后发生的退出异常不会自动否定已经保存的结果。
并发测试结果
固定模型和测试条件下,2—5 个 worker 均完成了 Run2 并发测试。关键数值签名在 1×10⁻⁸ 的绝对和相对容差内一致。
| worker 数 | 吞吐量/次·min⁻¹ | Aspen 峰值工作集/GiB | 最低可用内存/GiB |
|---|---|---|---|
| 2 | 2.69 | 2.03 | 11.88 |
| 3 | 3.45 | 2.96 | 11.10 |
| 4 | 3.91 | 3.45 | 10.72 |
| 5 | 4.37 | 3.90 | 10.44 |
从 4 个 worker 增加到 5 个 worker 后,吞吐量提高约 11.8%,同时可用内存继续下降。当前生产配置使用 4 个 worker,为操作系统和结果处理保留内存余量。5 个 worker 是本轮测试覆盖的最大数量,不代表当前机器或 Aspen Plus 的通用并发上限。
适用范围
上述结论来自当前 Aspen Plus V14、模型和机器环境。由于缺少 AspenTech 源码和调试符号,本文只记录可复现的故障边界和已验证的工程处理方式,不对 STRGXI2.dll 的内部根因作进一步判断。
Aspen Plus 补丁、版本、模型规模或硬件环境变化后,需要重新执行以下检查:
- 单实例计算与退出测试;
- 无归档互斥锁和有归档互斥锁的对照测试;
- 不同 worker 数量的吞吐量、内存和数值一致性测试;
- 最终候选的独立 fresh 验证。