Qt 的 QSqlDatabase 不能跨线程复用
目录
发生了什么
关键链路(一句话版)
两种修法(选一种即可)
方案 A(推荐):每个采集线程自建 DB 连接
方案 B(更快见效):先让曲线不依赖写库成功
修改后你应看到
极简代码提示
发生了什么
-
前端每秒拉数据,但模型一直是空的 → 曲线不动。
-
根因:采集线程里直接用主线程的数据库连接写库,Qt 不允许跨线程复用连接 ⇒
saveToDatabase()失败 ⇒ 不发dataCollected⇒ 模型无数据。
关键链路(一句话版)
DataCollector.onRegistersRead() → 解析 → saveToDatabase()
-
成功:
emit dataCollected(...)(现在只有成功才发) -
失败:不发 → 模型空 → 曲线空
两种修法(选一种即可)
方案 A(推荐):每个采集线程自建 DB 连接
-
新增
DatabaseService::acquireConnection(QString name) -
在
DataCollector::run()里:用线程ID做连接名创建并open();只在本线程用;退出时close()+removeDatabase(name) -
这样
saveToDatabase()不再跨线程,能成功,dataCollected会发出。
方案 B(更快见效):先让曲线不依赖写库成功
-
把
emit dataCollected(dp);从if (saveToDatabase(dp)) emit dataCollected(dp);改成
emit dataCollected(dp); asyncSaveToDatabase(dp); // 失败也不影响曲线 -
写库可放到主线程/专用DB线程异步处理。
修改后你应看到
-
连接成功后按钮变“断开连接”。
-
日志有“DataCollector: 采集成功”。
-
曲线逐点更新。
极简代码提示
A 方案:
// DataCollector::run()
auto name = QString("conn_%1").arg(reinterpret_cast<quintptr>(QThread::currentThreadId()));
QSqlDatabase db = DatabaseService::instance()->acquireConnection(name);
// ... use db in this thread ...
// on exit:
db.close();
QSqlDatabase::removeDatabase(name);
B 方案:
emit dataCollected(dp); // 先推模型
QMetaObject::invokeMethod(dbWorker, "save", Qt::QueuedConnection, Q_ARG(DataPoint, dp)); // 异步写库
一句话总结:跨线程复用数据库连接导致写库失败→不发数据→曲线空。要么给采集线程单独建连接(最佳),要么先不把发数依赖写库成功(最快)。
