我有一个用C ++编写的多线程Qt5应用程序,其中每个线程都写入同一个数据库。
我在线程之间共享相同的数据库QSqlDatabase变量,但每个线程都创建自己的查询。我经常遇到崩溃,我在崩溃时抓住了这些信息:
Driver error [QMYSQL3: Unable to store statement results]
Database error [Commands out of sync; you can't run this command now]
Type [2]
Number [2014]
首先,我可以在多个线程中同时进行MySQL查询吗?如果是这样,我是否需要使用互斥锁保护SQL调用?或者我是否需要发布更多信息....
更新:我有一个单独的DBManager线程来处理打开/关闭数据库,以及简单的数据库写入。我这样做是因为我的数据库经常脱机,我不希望其他线程在db打开失败时挂起最多2分钟。不,我有更多的线程可以执行报告,并且必须从数据库中检索大量数据。
如下所述,不允许跨线程共享db句柄。所以现在也许这更像是一个设计问题 - 处理这种情况的最佳方法是什么?我不希望每个执行数据库访问的线程都尝试自己打开并等待2分钟,以防数据库脱机
答案 0 :(得分:2)
我通常通过在我的数据库管理器类上提供一个工厂方法来处理这个问题,这看起来与此类似(半伪代码):
static QHash<QThread, QSqlDatabase> DatabaseManager::s_instances;
static QMutex DatabaseManager::s_databaseMutex;
QSqlDatabase DatabaseManager::database() {
QMutexLocker locker(s_databaseMutex);
QThread *thread = QThread::currentThread();
// if we have a connection for this thread, return it
if (s_instances.contains(thread))
return s_instances[thread];
}
// otherwise, create a new connection for this thread
QSqlDatabase connection =
QSqlDatabase::cloneDatabase(existingConnection, uniqueNameForNewInstanceOnThisThread);
// open the database connection
// initialize the database connection
s_instances.insert(thread, connection);
return connection;
}
只要您确保创建初始连接(您可以修改工厂以包含它,或者只是在DatabaseManager构造函数中创建初始连接),您应该能够替换您正在使用的大多数现有案例带有:
的QSqlDatabaseQSqlQuery query(DatabaseManager::database());
query.exec(<some sql>);
不用担心你要拨打哪个线程。希望有所帮助!
答案 1 :(得分:1)
阅读SQL模块的文档,特别是threads section,它指出每个连接只能在创建它的线程中使用。
因此添加互斥锁是不够的,您必须在每个线程中创建一个新的连接对象,或者将所需的数据传递给执行所有数据库查询的专用线程。
答案 2 :(得分:0)
这是一个很好的答案,只有很小的改变。此代码应完整且可直接编译:
database_manager.h:
#ifndef DATABASEMANAGER_H
#define DATABASEMANAGER_H
#include <QMutex>
#include <QHash>
#include <QSqlDatabase>
class QThread;
class DatabaseManager
{
public:
static QSqlDatabase database(const QString& connectionName = QLatin1String(QSqlDatabase::defaultConnection));
private:
static QMutex s_databaseMutex;
static QHash<QThread*, QSqlDatabase> s_instances;
};
#endif // DATABASEMANAGER_H
database_manager.cpp:
#include "database_manager.h"
#include <QSqlDatabase>
#include <QMutexLocker>
#include <QThread>
#include <stdexcept>
QMutex DatabaseManager::s_databaseMutex;
QHash<QThread*, QHash<QString, QSqlDatabase>> DatabaseManager::s_instances;
QSqlDatabase DatabaseManager::database(const QString& connectionName)
{
QMutexLocker locker(&s_databaseMutex);
QThread *thread = QThread::currentThread();
// if we have a connection for this thread, return it
auto it_thread = s_instances.find(thread);
if (it_thread != s_instances.end()) {
auto it_conn = it_thread.value().find(connectionName);
if (it_conn != it_thread.value().end()) {
return it_conn.value();
}
}
// otherwise, create a new connection for this thread
QSqlDatabase connection = QSqlDatabase::cloneDatabase(
QSqlDatabase::database(connectionName),
QString("%1_%2").arg(connectionName).arg((int)thread));
// open the database connection
// initialize the database connection
if (!connection.open()) {
throw std::runtime_error("Unable to open the new database connection.");
}
s_instances[thread][connectionName] = connection;
return connection;
}
用法:使用DatabaseManager :: database()
代替QSqlDatabase :: database()编辑2016-07-01:修复了多个连接名称的错误