Qt5.15.2中无锁QMap多线程读写是否安全?是否会崩溃?
问题解答:Qt5.15.2中QMap跨线程读写无锁是否安全?
结论:完全不安全,极大概率会导致程序崩溃或出现不可预期的异常行为
原因分析
Qt 5.x中的QMap属于非线程安全容器,它的所有成员函数都没有针对并发读写做线程安全设计:
- 子线程执行
g_map[...].insert(...)时,会修改QMap内部的红黑树结构(插入节点、调整树平衡)。 - 主线程同时调用
g_map.keys()或g_map.value(key)时,依赖QMap内部结构的稳定性。此时如果子线程正在修改结构,会直接破坏遍历逻辑,导致迭代器失效、内存访问错误,最终触发崩溃。 - 即使侥幸没崩溃,也会出现读取到半修改状态的"脏数据",比如某个key对应的内部QMap只插入了一半,主线程读到不完整的内容。
你的代码示例
子线程代码:
QMap<QString, QMap<QString, bool>> g_map; void subThread::run() { while (!m_stop) { g_map[QDateTime::currentDateTime().toString()].insert(QDateTime::currentDateTime().toString(), flag); flag = !flag; } }
主线程代码:
void MainThread::run() { for (auto key : g_map.keys()) { qDebug() << key << " " << g_map.value(key); } }
修复方案
推荐两种可靠的解决方式:
使用读写锁(适合多读少写场景)
用QReadWriteLock,子线程写操作加写锁,主线程读操作加读锁,这样多个读线程可以同时访问,写操作独占访问,效率更高:QReadWriteLock g_mapLock; QMap<QString, QMap<QString, bool>> g_map; // 子线程写操作 void subThread::run() { while (!m_stop) { QWriteLocker locker(&g_mapLock); g_map[QDateTime::currentDateTime().toString()].insert(QDateTime::currentDateTime().toString(), flag); flag = !flag; } } // 主线程读操作 void MainThread::run() { QReadLocker locker(&g_mapLock); for (auto key : g_map.keys()) { qDebug() << key << " " << g_map.value(key); } }用信号槽传递数据(避免跨线程直接操作容器)
子线程不直接修改全局QMap,而是将修改后的数据通过信号发送给主线程,主线程在自己的线程上下文里更新本地QMap并读取,完全避免跨线程竞争:// 子线程类定义 class subThread : public QThread { Q_OBJECT signals: void updateMap(const QString& outerKey, const QString& innerKey, bool value); // ...其他成员 }; // 子线程run函数 void subThread::run() { while (!m_stop) { QString outerKey = QDateTime::currentDateTime().toString(); QString innerKey = QDateTime::currentDateTime().toString(); emit updateMap(outerKey, innerKey, flag); flag = !flag; QThread::msleep(1); // 避免过度发送信号 } } // 主线程接收信号 MainThread::MainThread(QObject *parent) : QThread(parent) { connect(subThreadInst, &subThread::updateMap, this, [this](const QString& outerKey, const QString& innerKey, bool value){ g_map[outerKey].insert(innerKey, value); }); } // 主线程读操作(此时是在自己线程,直接访问无风险) void MainThread::run() { while (true) { // 读取前可以加个短延时,按需调整 QThread::msleep(100); for (auto key : g_map.keys()) { qDebug() << key << " " << g_map.value(key); } } }
内容的提问来源于stack exchange,提问作者MoonBreak Darkness
相关产品推荐
相关产品推荐

