Merge pull request 'docs: 记录管线/管件初始化 sqlite 访问的分析与缓存方案' (#88) from company-desktop/company-desktop-computer/perf-pipe-style-sqlite-cache into main
Reviewed-on: #88 Reviewed-by: gjm <littleoldpan@163.com>
This commit was merged in pull request #88.
This commit is contained in:
commit
dc6bf80e9c
2 files changed
+389
-34
No files matched your search
@@ -4911,7 +4911,15 @@ int CoreTools::getCurrentTrade()
|
||||
|
||||
CString CoreTools::getCurrentPipeSysTblName()
|
||||
{
|
||||
//表名随行业变化,所以按 getCurrentTrade() 缓存;
|
||||
//指针故意不释放,避免 DLL 卸载时的析构顺序问题
|
||||
static int *s_pCachedTrade = new int(-1);
|
||||
static CString *s_pCachedTable = new CString;
|
||||
|
||||
int nTrade = getCurrentTrade();
|
||||
if (*s_pCachedTrade == nTrade && !s_pCachedTable->IsEmpty())
|
||||
return *s_pCachedTable;
|
||||
|
||||
CString sRel(_T("WConfig_CN"));
|
||||
CUIISQLiteServer db;
|
||||
if (!db.InitADOServer(FilePath::GetSysDir() + _T("\\Plumbing.db"))) return sRel;
|
||||
@@ -4924,36 +4932,106 @@ CString CoreTools::getCurrentPipeSysTblName()
|
||||
|
||||
|
||||
db.GetSelData(sql, sRel);
|
||||
|
||||
*s_pCachedTrade = nTrade;
|
||||
*s_pCachedTable = sRel;
|
||||
return sRel;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
//一个管线系统的四个样式字段,全部按字符串缓存
|
||||
struct PipeSysStyle
|
||||
{
|
||||
CString sColor; //颜色
|
||||
CString sLt; //线型名
|
||||
CString sLw; //线宽
|
||||
CString sLy; //图层名
|
||||
};
|
||||
|
||||
typedef std::map<CString, PipeSysStyle> PipeSysStyleMap;
|
||||
|
||||
//指针故意不释放,避免 DLL 卸载时的析构顺序问题
|
||||
PipeSysStyleMap& PipeSysStyleCache()
|
||||
{
|
||||
static PipeSysStyleMap *s_pMap = new PipeSysStyleMap;
|
||||
return *s_pMap;
|
||||
}
|
||||
|
||||
//一条 SQL 取全四个字段;数据库打不开返回 false
|
||||
bool QueryPipeSysStyle(LPCTSTR pszSys, bool bCenter, PipeSysStyle& st)
|
||||
{
|
||||
CUIISQLiteServer db;
|
||||
if (!db.InitADOServer(FilePath::GetSysDir() + _T("\\Plumbing.db"))) return false;
|
||||
|
||||
CString sTable(CoreTools::getCurrentPipeSysTblName());
|
||||
CString pipesys = bCenter ? _T("管线中心线") : _T("管线外边线");
|
||||
|
||||
CString sql;
|
||||
sql.Format(_T("SELECT [COLOR],[LINETYPE],[LINEWIDTH],[LAYNAME] FROM %s WHERE [SYSTEM]='%s' AND [PIPESYS]='%s'")
|
||||
, sTable, pszSys, pipesys);
|
||||
|
||||
CStringArray arVals;
|
||||
db.GetSelData(sql, arVals);
|
||||
|
||||
if (arVals.GetSize() < 4)
|
||||
{ //查不到:沿用原来的默认值,也照样缓存,避免配错的系统名反复查库
|
||||
st.sColor = _T("256");
|
||||
st.sLt = _T("Continuous");
|
||||
st.sLw = _T("0");
|
||||
st.sLy = _T("0");
|
||||
return true;
|
||||
}
|
||||
|
||||
st.sColor = arVals.GetAt(0);
|
||||
st.sLt = arVals.GetAt(1);
|
||||
st.sLw = arVals.GetAt(2);
|
||||
st.sLy = arVals.GetAt(3);
|
||||
return true;
|
||||
}
|
||||
|
||||
//进程级缓存,key = 表名 + 系统名 + 中心线/外边线;
|
||||
//只缓存字符串,AcDbObjectId 属于当前图纸,由调用方每次现解析
|
||||
bool GetPipeSysStyle(LPCTSTR pszSys, bool bCenter, PipeSysStyle& st)
|
||||
{
|
||||
CString sKey;
|
||||
sKey.Format(_T("%s\x01%s\x01%d")
|
||||
, (LPCTSTR)CoreTools::getCurrentPipeSysTblName(), pszSys, bCenter ? 1 : 0);
|
||||
|
||||
PipeSysStyleMap& mapCache = PipeSysStyleCache();
|
||||
PipeSysStyleMap::const_iterator it = mapCache.find(sKey);
|
||||
if (it != mapCache.end())
|
||||
{
|
||||
st = it->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
PipeSysStyle stNew;
|
||||
if (!QueryPipeSysStyle(pszSys, bCenter, stNew)) return false;
|
||||
|
||||
mapCache[sKey] = stNew;
|
||||
st = stNew;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
int CoreTools::getColorByPipeSys(LPCTSTR sys, bool bCenter)
|
||||
{
|
||||
CUIISQLiteServer db;
|
||||
if (!db.InitADOServer(FilePath::GetSysDir() + _T("\\Plumbing.db"))) return 256;
|
||||
PipeSysStyle st;
|
||||
if (!GetPipeSysStyle(sys, bCenter, st)) return 256;
|
||||
|
||||
CString sTable(CoreTools::getCurrentPipeSysTblName());
|
||||
|
||||
CString pipesys = bCenter ? _T("管线中心线") : _T("管线外边线");
|
||||
CString sql;
|
||||
sql.Format(_T("SELECT [COLOR] FROM %s WHERE [SYSTEM]='%s' AND [PIPESYS]='%s'"), sTable, sys, pipesys);
|
||||
CString sClr(_T("256"));
|
||||
db.GetSelData(sql, sClr);
|
||||
return _ttoi(sClr);
|
||||
return _ttoi(st.sColor);
|
||||
}
|
||||
|
||||
AcDbObjectId CoreTools::getLtByPipeSys(LPCTSTR sys, bool bCenter)
|
||||
{
|
||||
AcDbObjectId id(AcDbObjectId::kNull);
|
||||
CUIISQLiteServer db;
|
||||
if (!db.InitADOServer(FilePath::GetSysDir() + _T("\\Plumbing.db"))) return id;
|
||||
|
||||
CString sTable(CoreTools::getCurrentPipeSysTblName());
|
||||
CString pipesys = bCenter ? _T("管线中心线") : _T("管线外边线");
|
||||
CString sql;
|
||||
sql.Format(_T("SELECT [LINETYPE] FROM %s WHERE [SYSTEM]='%s' AND [PIPESYS]='%s'"), sTable, sys, pipesys);
|
||||
CString sLt(_T("Continuous"));
|
||||
db.GetSelData(sql, sLt);
|
||||
PipeSysStyle st;
|
||||
if (!GetPipeSysStyle(sys, bCenter, st)) return id;
|
||||
|
||||
CString sLt(st.sLt);
|
||||
if (sLt.IsEmpty()) sLt = _T("Continuous");
|
||||
|
||||
AcDbDatabase *pDb = acdbCurDwg();
|
||||
if (!pDb) return id;
|
||||
@@ -4977,15 +5055,11 @@ AcDbObjectId CoreTools::getLtByPipeSys(LPCTSTR sys, bool bCenter)
|
||||
|
||||
double CoreTools::getLwByPipeSys(LPCTSTR sys, bool bCenter)
|
||||
{
|
||||
CUIISQLiteServer db;
|
||||
if (!db.InitADOServer(FilePath::GetSysDir() + _T("\\Plumbing.db"))) return 0;
|
||||
PipeSysStyle st;
|
||||
if (!GetPipeSysStyle(sys, bCenter, st)) return 0;
|
||||
|
||||
CString sTable(CoreTools::getCurrentPipeSysTblName());
|
||||
CString pipesys = bCenter ? _T("管线中心线") : _T("管线外边线");
|
||||
CString sql;
|
||||
sql.Format(_T("SELECT [LINEWIDTH] FROM %s WHERE [SYSTEM]='%s' AND [PIPESYS]='%s'"), sTable, sys, pipesys);
|
||||
CString sLw(_T("0"));
|
||||
db.GetSelData(sql, sLw);
|
||||
CString sLw(st.sLw);
|
||||
if (sLw.IsEmpty()) sLw = _T("0");
|
||||
|
||||
AecDbObjXDataMap xdata;
|
||||
if (KTModelLayoutXData::ReadModelXData(KT_PIPEINIT, xdata))
|
||||
@@ -5007,15 +5081,12 @@ double CoreTools::getLwByPipeSys(LPCTSTR sys, bool bCenter)
|
||||
AcDbObjectId CoreTools::getLyByPipeSys(LPCTSTR sys, bool bCenter, bool bUpdate)
|
||||
{
|
||||
AcDbObjectId id;
|
||||
CUIISQLiteServer db;
|
||||
if (!db.InitADOServer(FilePath::GetSysDir() + _T("\\Plumbing.db"))) return id;
|
||||
|
||||
CString sTable(CoreTools::getCurrentPipeSysTblName());
|
||||
CString pipesys = bCenter ? _T("管线中心线") : _T("管线外边线");
|
||||
CString sql;
|
||||
sql.Format(_T("SELECT [LAYNAME] FROM %s WHERE [SYSTEM]='%s' AND [PIPESYS]='%s'"), sTable, sys, pipesys);
|
||||
CString sLy(_T("0"));
|
||||
db.GetSelData(sql, sLy);
|
||||
PipeSysStyle st;
|
||||
if (!GetPipeSysStyle(sys, bCenter, st)) return id;
|
||||
|
||||
CString sLy(st.sLy);
|
||||
if (sLy.IsEmpty()) sLy = _T("0");
|
||||
|
||||
AcDbDatabase *pDb = acdbCurDwg();
|
||||
if (!pDb) return id;
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
# AecDbEwPipe / AecDbEwFitting 初始化时的 sqlite 访问分析与修复方案
|
||||
|
||||
> 结论:这两个类本身不直接调用 sqlite。初始化阶段(`dwgInFields`,即打开图纸加载实体时)的大量 sqlite 访问全部通过 `CoreTools` 的 4 个样式查询 getter 间接产生。
|
||||
>
|
||||
> 状态(2026-10-27):第 7 节方案已落地到 `CoreCommand/CoreTools.cpp`,R190 Debug 下 `AecRxBase` / `CoreCommand_BAL_REMOTE` / `CoreLibrary_BAL_REMOTE` / `CoreImplement` 四个工程编译通过。详见第 12 节。
|
||||
|
||||
---
|
||||
|
||||
## 1. 直接结论
|
||||
|
||||
- `AecDbEwPipe.cpp` / `AecDbEwFitting.cpp` / `AecDbEwPipeBase.cpp` 中**没有任何** `CUIISQLiteServer` / `InitADOServer` / `GetSelData` 直接调用。
|
||||
- 这两个类的 sqlite 访问 100% 走 `CoreTools` 的 4 个 getter:
|
||||
- `getColorByPipeSys`
|
||||
- `getLtByPipeSys`
|
||||
- `getLwByPipeSys`
|
||||
- `getLyByPipeSys`
|
||||
- 构造函数很轻(只 `SetAt` 几个 XData 键,不碰库);`dxfInFields` 直接返回 `eOk`;`GetPipeParam` / `GetFittingName` 等也只读内存里的 `m_mapSpecXData`。
|
||||
|
||||
## 2. 初始化热点在 `dwgInFields`
|
||||
|
||||
`AecDbEwPipe` 和 `AecDbEwFitting` 在加载实体时,`dwgInFields` 里有一段补样式键的代码:
|
||||
|
||||
- `AecDbEwPipe::dwgInFields` —— `CoreLibrary/AecDbEwPipe.cpp:990-1074`
|
||||
- `AecDbEwFitting::dwgInFields` —— `CoreLibrary/AecDbEwFitting.cpp:609-680`
|
||||
|
||||
两段代码几乎一模一样:
|
||||
|
||||
```cpp
|
||||
CString sSys;
|
||||
if (m_mapSpecXData.GetAt(PIPE_SYS, sSys))
|
||||
{
|
||||
int nvt;
|
||||
if (!m_mapSpecXData.HasKey(PIPE_COLOR, nvt)) { clr = CoreTools::getColorByPipeSys(sSys, true); ... }
|
||||
if (!m_mapSpecXData.HasKey(PIPE_LINETYPE, nvt)) { idLt = CoreTools::getLtByPipeSys(sSys, true); ... }
|
||||
if (!m_mapSpecXData.HasKey(PIPE_LINELAYER, nvt)) { idLy = CoreTools::getLyByPipeSys(sSys, true); ... }
|
||||
if (!m_mapSpecXData.HasKey(PIPE_LINEWIDTH, nvt)) { dw = CoreTools::getLwByPipeSys(sSys, true); ... }
|
||||
// 外边线再来一遍(getXxx(..., false))
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
即:**只要 XData 里 `PIPE_SYS` 存在、而某个样式键缺失,就逐键调用 4 个 getter,中心线 + 外边线一共最多 8 次。**
|
||||
|
||||
## 3. 每个 getter 的开销(已在 CoreTools.cpp 确认)
|
||||
|
||||
`getColorByPipeSys / getLtByPipeSys / getLwByPipeSys / getLyByPipeSys`
|
||||
(`CoreCommand/CoreTools.cpp:4930 / 4945 / 4978 / 5007`)每个都:
|
||||
|
||||
1. `new CUIISQLiteServer`(构造里 `CoInitialize`)
|
||||
2. `InitADOServer(...\Plumbing.db)` → `sqlite3_open`
|
||||
3. 调 `getCurrentPipeSysTblName()`(`CoreTools.cpp:4912`)——**又新建一个 server、又 open 一次库**,再 `SELECT [TABLENAME] FROM WPipeCfg`
|
||||
4. 自己再跑一条 `SELECT [COLOR|LINETYPE|LINEWIDTH|LAYNAME] FROM ...`
|
||||
5. 析构 → `sqlite3_close` + `CoUninitialize`
|
||||
|
||||
⇒ **1 个 getter ≈ 2 次 `sqlite3_open` + 2 条 SQL + 2 次 COM 初始化/反初始化。**
|
||||
|
||||
`dwgInFields` 里 8 个 getter = **每个实体最多 ~16 次 open + 16 条 SQL**。且 `getCurrentPipeSysTblName()` 被重复调用 8 次,每次都独立开一次库查 `WPipeCfg`。
|
||||
|
||||
## 4. 更糟的点:`getLyByPipeSys` 会递归再查库
|
||||
|
||||
`getLyByPipeSys`(`CoreTools.cpp:5007`)里,当图层不存在需要新建时,还会再调 `getLtByPipeSys` + `getColorByPipeSys`(各再 +2 open +2 SQL)。因此单次 `getLyByPipeSys` 最坏可达 **6 次 open + 6 条 SQL**。图纸里图层没建全时,`dwgInFields` 的开销会进一步放大。
|
||||
|
||||
## 5. 量级估算
|
||||
|
||||
打开一张有 N 根管 + M 个管件的图纸:
|
||||
|
||||
- 样式键已写全(正常保存的图):`HasKey` 全部命中,**不查库**。
|
||||
- 样式键缺失(老版本图 / 外来图 / 首次由旧版数据升级):每个实体最多 ~16 次 open。几百个实体就是**几千次串行的 `sqlite3_open`**,这就是"初始化非常慢"的直接来源。
|
||||
|
||||
## 6. 调用点统计(不含 CoreTools.cpp 内部)
|
||||
|
||||
| 函数 | 独立调用点 | 是否开库 |
|
||||
| --- | --- | --- |
|
||||
| `getCurrentPipeSysTblName` | 46 | **每次都开库** |
|
||||
| `getColorByPipeSys` | 23 | 是 |
|
||||
| `getLtByPipeSys` | 21 | 是 |
|
||||
| `getLwByPipeSys` | 41 | 是 |
|
||||
| `getLyByPipeSys` | 61 | 是(且递归再查) |
|
||||
| `getLyForPipe` | 24 | 否(只操作图层表) |
|
||||
| `getPipeColorBySystem` | 1 | 是 |
|
||||
|
||||
合计约 192 处热点调用,全部收敛到 `CoreTools.cpp`,**改这一个文件即全覆盖**。
|
||||
|
||||
## 7. 方案:进程级缓存(推荐,只改 `CoreTools.cpp`)
|
||||
|
||||
核心思路:把"4 条 SQL + 反复开库"压缩成"一次查询 + 内存缓存"。
|
||||
|
||||
### 7.1 第一步:缓存表名
|
||||
|
||||
`getCurrentPipeSysTblName()` 改成按行业缓存:
|
||||
|
||||
```cpp
|
||||
CString CoreTools::getCurrentPipeSysTblName()
|
||||
{
|
||||
static int s_cachedTrade = -1;
|
||||
static CString s_cachedTable;
|
||||
|
||||
int nTrade = getCurrentTrade();
|
||||
if (s_cachedTrade == nTrade && !s_cachedTable.IsEmpty())
|
||||
return s_cachedTable;
|
||||
|
||||
CString sRel(_T("WConfig_CN"));
|
||||
CUIISQLiteServer db;
|
||||
if (!db.InitADOServer(FilePath::GetSysDir() + _T("\\Plumbing.db"))) return sRel;
|
||||
|
||||
CString sql;
|
||||
if (nTrade == 6)
|
||||
sql.Format(_T("SELECT [TABLENAME] FROM WPipeCfg WHERE [STDNAME]='冶金'"));
|
||||
else
|
||||
sql.Format(_T("SELECT [TABLENAME] FROM WPipeCfg WHERE [STDNAME]='默认'"));
|
||||
db.GetSelData(sql, sRel);
|
||||
|
||||
s_cachedTrade = nTrade;
|
||||
s_cachedTable = sRel;
|
||||
return sRel;
|
||||
}
|
||||
```
|
||||
|
||||
key 带 `getCurrentTrade()` 是因为表名随行业(默认/冶金)变化;换图后 trade 变了会重新查,正确。
|
||||
|
||||
### 7.2 第二步:4 字段一条 SQL + 进程级缓存
|
||||
|
||||
在 `CoreTools.cpp` 的匿名 namespace 里加:
|
||||
|
||||
```cpp
|
||||
struct PipeSysStyle
|
||||
{
|
||||
CString sColor; // 颜色(字符串)
|
||||
CString sLt; // 线型名
|
||||
CString sLw; // 线宽
|
||||
CString sLy; // 图层名
|
||||
};
|
||||
|
||||
// 一条 SQL 取全 4 个字段
|
||||
bool QueryPipeSysStyle(LPCTSTR pszSys, bool bCenter, PipeSysStyle& st)
|
||||
{
|
||||
CUIISQLiteServer db;
|
||||
if (!db.InitADOServer(FilePath::GetSysDir() + _T("\\Plumbing.db"))) return false;
|
||||
|
||||
CString sTable = CoreTools::getCurrentPipeSysTblName();
|
||||
CString sPipeSys = bCenter ? _T("管线中心线") : _T("管线外边线");
|
||||
CString sql;
|
||||
sql.Format(_T("SELECT [COLOR],[LINETYPE],[LINEWIDTH],[LAYNAME] FROM %s WHERE [SYSTEM]='%s' AND [PIPESYS]='%s'"),
|
||||
(LPCTSTR)sTable, pszSys, (LPCTSTR)sPipeSys);
|
||||
|
||||
CStringArray ar;
|
||||
db.GetSelData(sql, ar);
|
||||
if (ar.GetSize() < 4)
|
||||
{
|
||||
st.sColor = _T("256"); st.sLt = _T("Continuous");
|
||||
st.sLw = _T("0"); st.sLy = _T("0");
|
||||
return true;
|
||||
}
|
||||
st.sColor = ar.GetAt(0); st.sLt = ar.GetAt(1);
|
||||
st.sLw = ar.GetAt(2); st.sLy = ar.GetAt(3);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 进程级缓存(key = 表名 + 系统名 + 中心线/外边线)
|
||||
bool GetPipeSysStyle(LPCTSTR pszSys, bool bCenter, PipeSysStyle& st)
|
||||
{
|
||||
static std::map<CString, PipeSysStyle> s_map;
|
||||
|
||||
CString key;
|
||||
key.Format(_T("%s\x01%s\x01%d"),
|
||||
(LPCTSTR)CoreTools::getCurrentPipeSysTblName(), pszSys, bCenter ? 1 : 0);
|
||||
|
||||
std::map<CString, PipeSysStyle>::iterator it = s_map.find(key);
|
||||
if (it != s_map.end()) { st = it->second; return true; }
|
||||
|
||||
PipeSysStyle stNew;
|
||||
if (!QueryPipeSysStyle(pszSys, bCenter, stNew)) return false;
|
||||
s_map[key] = stNew;
|
||||
st = stNew;
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
### 7.3 第三步:4 个 getter 改造成读缓存
|
||||
|
||||
```cpp
|
||||
int CoreTools::getColorByPipeSys(LPCTSTR sys, bool bCenter)
|
||||
{
|
||||
PipeSysStyle st;
|
||||
if (!GetPipeSysStyle(sys, bCenter, st)) return 256;
|
||||
return _ttoi(st.sColor);
|
||||
}
|
||||
```
|
||||
|
||||
`getLtByPipeSys` / `getLwByPipeSys` / `getLyByPipeSys` 保留原有副作用(解析线型 id、乘图纸比例、建/找图层),只是**数据来源换成 `st`**:
|
||||
|
||||
- `getLtByPipeSys`:拿 `st.sLt` 照旧去线型表解析,没有则 `loadLineType`;
|
||||
- `getLwByPipeSys`:拿 `st.sLw` 照旧乘图纸比例;
|
||||
- `getLyByPipeSys`:拿 `st.sLy` 照旧"没有就建图层、`bUpdate` 时刷新"。建图层时它内部还会调 `getLtByPipeSys`/`getColorByPipeSys`,但这俩现在**直接命中缓存,不再开库**,所以那段不用改。
|
||||
|
||||
`getPipeColorBySystem`(`CoreTools.cpp:4258`)后续也应改成走同一个 `GetPipeSysStyle` 缓存(当前只有 1 处调用且被注释,优先级低)。
|
||||
|
||||
## 8. 收益对比
|
||||
|
||||
| 场景 | 现在 | 改后 |
|
||||
| --- | --- | --- |
|
||||
| `dwgInFields` 8 个 getter(样式键缺失) | 最多 16+ 次 open + 16 条 SQL | 首次 2 次 open,之后 0 次 |
|
||||
| `getCurrentPipeSysTblName` 46 处调用 | 每次 1 次 open | 首次 1 次,之后 0 次 |
|
||||
| 一张图几百个实体加载 | 几千次串行 open | 约 2 次 |
|
||||
| Jig 拖动每帧 | 16 次 open | 首次 2 次,之后 0 次 |
|
||||
|
||||
## 9. 取舍与风险
|
||||
|
||||
| 取舍/风险 | 处理 |
|
||||
| --- | --- |
|
||||
| 缓存只存字符串,不存 `AcDbObjectId` | 图层/线型 id 是"当前图纸"的,多文档切换后会失效,必须每次现解析(`getLtByPipeSys`/`getLyByPipeSys` 里的图纸操作保留) |
|
||||
| 改了系统样式配置后读到旧值 | 缓存生命周期 = 进程,重启软件生效。后续可在配置对话框保存后加 `InvalidatePipeSysStyleCache()` |
|
||||
| 配错的系统名被永久缓存 | 查不到也缓存默认值,避免反复查库;重启即可恢复 |
|
||||
| 多线程 | 按单线程实现(ARX 命令在主线程);若担心给 `s_map` 加 `CCriticalSection` |
|
||||
| 内存 | 条目数 = 系统名 × 2,通常个位数,可忽略 |
|
||||
| 老 preset 编译 | 用 `std::map` + C++03 语法,兼容 R18~R26 全套 preset |
|
||||
|
||||
## 10. 可选第二层:常驻只读连接(暂不建议先做)
|
||||
|
||||
如果想连"首次 2 次 open"也省掉,或者覆盖那 46 处不走 4 个 getter 的其它查询,可以给 `Plumbing.db` 做一个进程级常驻只读 `CUIISQLiteServer` 单例。
|
||||
|
||||
但这层风险更高,暂缓:
|
||||
|
||||
- `CUIISQLiteServer` 构造/析构里绑了 `CoInitialize`/`CoUninitialize`,单例要自己管理 COM 生命周期;
|
||||
- sqlite3 同一连接对象跨线程并发不安全,需要锁或每线程连接;
|
||||
- 现有大量写操作依赖"用完即 close"来落盘/释放写锁,常驻连接只适合只读,写路径不能动。
|
||||
|
||||
## 11. 实施与验证
|
||||
|
||||
实施顺序:
|
||||
|
||||
1. **先做第 7 节第一层**(`CoreTools.cpp` 一处改动):覆盖 192 处调用点里的全部热点,`dwgInFields` 和绘制两条路径一起解决。
|
||||
2. 用 MSVC 性能分析对比改前/改后的 `sqlite3_open` 调用次数。
|
||||
3. 功能回归:
|
||||
- 管线中心线 / 外边线的颜色、线型、线宽、图层与改之前一致;
|
||||
- 画一根带法兰的管段 + 弯头 + 三通,实体显示正常;
|
||||
- 打开两张图纸各画一段,确认图层/线型不串图纸;
|
||||
- 打开老版本图纸(样式键缺失)加载正常。
|
||||
4. 编码自检:`CoreTools.cpp` 保持 UTF-8 BOM。
|
||||
|
||||
涉及文件:
|
||||
|
||||
- **修改**:`SourceCode/Code2026/CoreCommand/CoreTools.cpp`(匿名 namespace 加 `PipeSysStyle` / `QueryPipeSysStyle` / `GetPipeSysStyle`;改 `getCurrentPipeSysTblName` + 4 个 getter)
|
||||
- **不动**:`inc/CoreTools.h`(函数签名不变)、`sqliteserver` 工程、`CoreLibrary` / `PipelineCmd` / `PipelineSpecCmd` 业务代码
|
||||
|
||||
## 12. 实施记录(2026-10-27)
|
||||
|
||||
### 12.1 实际改动(仅 `CoreCommand/CoreTools.cpp`,+106 / -35 行)
|
||||
|
||||
| 位置 | 改动 |
|
||||
| --- | --- |
|
||||
| `getCurrentPipeSysTblName`(约 4912) | 按 `getCurrentTrade()` 缓存表名;静态指针故意不释放,避开 DLL 卸载期析构顺序问题 |
|
||||
| 新增匿名 namespace(4 个 getter 之前) | `PipeSysStyle`、`PipeSysStyleMap`、`PipeSysStyleCache()`、`QueryPipeSysStyle()`、`GetPipeSysStyle()` |
|
||||
| `getColorByPipeSys` | 改为读缓存,库打不开仍返回 256(保持原语义) |
|
||||
| `getLtByPipeSys` | 线型名取自缓存,后续「线型表解析 → `loadLineType`」逻辑原样保留 |
|
||||
| `getLwByPipeSys` | 线宽取自缓存,后续「乘图纸比例」逻辑原样保留 |
|
||||
| `getLyByPipeSys` | 图层名取自缓存,后续「建图层 / `bUpdate` 刷新」逻辑原样保留 |
|
||||
|
||||
实现细节与第 7 节草案的差异:
|
||||
|
||||
- 缓存容器用 `static ... *s_pMap = new ...` 的**故意泄漏**写法(而非函数内静态对象),避免 MFC 对象在 DLL 卸载阶段析构导致的崩溃风险;`getCurrentPipeSysTblName` 的缓存同样处理。
|
||||
- 保留「库打不开就早退」的原行为:`GetPipeSysStyle` 返回 `false` 时,`getColorByPipeSys` 返回 256、`getLtByPipeSys`/`getLyByPipeSys` 返回空 id、`getLwByPipeSys` 返回 0——与改动前一致。
|
||||
- `getPipeColorBySystem`(`CoreTools.cpp:4258`)**未改**。它的 SQL 不带 `PIPESYS` 过滤、取的是「任意一行」的颜色,语义与 4 个 getter 不同,改成走缓存会改变取值;目前只有 1 处调用且调用点被注释,收益低、风险高。但它复用 `getCurrentPipeSysTblName()`,已顺带省掉一次开库。
|
||||
|
||||
### 12.2 编译验证
|
||||
|
||||
R190(AutoCAD 2019,C++98,`v142`)Configure + Debug 编译通过:
|
||||
|
||||
```bash
|
||||
cd SourceCode/Code2026
|
||||
cmake --preset R190
|
||||
cmake --build --preset R190 --config Debug --target AecRxBase
|
||||
cmake --build --preset R190 --config Debug --target CoreCommand_BAL_REMOTE
|
||||
cmake --build --preset R190 --config Debug --target CoreLibrary_BAL_REMOTE
|
||||
cmake --build --preset R190 --config Debug --target CoreImplement
|
||||
```
|
||||
|
||||
四个目标全部链接成功(`CoreCommand_BAL_REMOTE` 内已重新编译 `CoreTools.cpp`,无新增 warning)。
|
||||
|
||||
### 12.3 待做
|
||||
|
||||
- 在 CAD 中加载 `CoreCommand.arx` 后实测:管线中心/外边线的颜色、线型、线宽、图层与改前一致;老图纸(样式键缺失)加载正常;两张图切换不串图层/线型。
|
||||
- 用 MSVC 性能分析对比 `sqlite3_open` / `sqlite3_exec` 调用次数。
|
||||
- 若后续需要「改配置立即生效」,在系统样式配置保存处加 `InvalidatePipeSysStyleCache()`(清空 `PipeSysStyleCache()`)。
|
||||
Reference in new issue
Block a user