我正在尝试使用线程从MySQL数据库中获取数据,但是在调用:
之后QThread* thread = new QThread;
Beacon *beacon = new Beacon(m_db, begin, end);
beacon->moveToThread(thread);
connect(beacon, &Beacon::values, this, &Tm::insertRow);
connect(beacon, &Beacon::finished, thread, &QThread::quit);
connect(beacon, &Beacon::finished, beacon, &Beacon::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
connect(thread, &QThread::started, beacon, &Beacon::executeQuerySmooth);
connect(beacon, &Beacon::finished, this, &Tm::finished);
thread->start();
我明白了:
QMYSQLResult::cleanup: unable to free statement handle
下次尝试使用连接时我得到了:
QSqlError("2006", "QMYSQL: Unable to execute query", "MySQL server has gone away")
可能会发生什么?
答案 0 :(得分:2)
您只能使用创建它的线程(reference)的连接,或者通常仅使用一个线程的连接。
您可以采取两种方法:
在专用线程中维护数据库连接,并从那里使用它。无论如何,使用短线程是一种过早的悲观,因此开始时这是一个糟糕的方法。
从线程池中移动工作线程之间的数据库连接。使用QtConcurrent::run
在线程池的线程上运行仿函数。
注意:Beacon::Beacon(...)
构造函数无法使用数据库,它只能存储一个引用/指针!
有关postCall
的更多信息,请参阅this answer。
template <typename T>
void postCall(QThread * thread, T && functor) {
QObject source;
QObject::connect(&source, &QObject::destroyed,
QEventDispatcher::instance(thread), std::forward(functor));
}
class Tm : public QObject {
Q_OBJECT
QMutex m_dbMutex;
QSqlDatabase m_db;
Q_SIGNAL void openFailed();
Q_SIGNAL void openSucceeded();
public:
void openConnection() {
QMutexLocker lock(&m_dbMutex);
m_db.addDatabase("QSQLITE");
... // set database's properties
m_db.moveToThread(0); // we don't know what thread it will be used in
lock.unlock();
QtConcurrent::run([this]{
QMutexLocker lock(&m_dbMutex);
m_db.moveToThread(QThread::currentThread());
bool rc = m_db.open();
if (rc) {
m_db.moveToThread(0);
emit openSucceeded();
} else {
m_db.moveToThread(this->thread());
emit openFailed();
}
});
}
void beaconate() {
...
QSharedPointer<Beacon> beacon(new Beacon(&m_db, begin, end));
connect(beacon, &Beacon::values, this, &Tm::insertRow);
connect(beacon, &Beacon::finished, this, &Tm::finished);
beacon->setMoveToThread(0);
QtConcurrent::run([beacon]{
beacon->moveToThread(QThread::currentThread());
QMutexLocker lock(&m_dbMutex);
m_db.moveToThread(QThread::currentThread());
QEventLoop loop; // only if Beacon needs one
connect(beacon, &Beacon::finished, &loop, &QEventLoop::quit);
beacon->executeQuerySmooth();
loop.exec();
m_db.moveToThread(0);
});
}
~Tm() {
QMutexLocker lock(&m_dbMutex);
m_db.moveToThread(thread());
}
...
};
class Thread : public QThread
{ using QThread::run; public: ~Thread() { quit(); wait(); } };
class Tm : public QObject {
Q_OBJECT
QSqlDatabase m_db;
Thread m_dbThread; // must be declared after m_db, so that it's destructed prior to m_db
Q_SIGNAL void openFailed();
Q_SIGNAL void openSucceeded();
public:
Tm(QObject * parent = 0) : QObject(parent) {
m_dbThread.start();
}
void openConnection() {
m_db.addDatabase("QSQLITE");
... // set database's properties
m_db.moveToThread(&m_dbThread);
postCall(&m_dbThread, [this]{
if (! m_db.open()) {
m_db.moveToThread(this->thread());
emit openFailed();
} else
emit openSucceeded();
});
}
void beaconate() {
...
auto beacon = new Beacon(&m_db, begin, end);
beacon.moveToThread(&m_dbThread);
connect(beacon, &Beacon::values, this, &Tm::insertRow);
connect(beacon, &Beacon::finished, beacon, &Beacon::deleteLater);
connect(beacon, &Beacon::finished, this, &Tm::finished);
postCall(&m_dbThread, [beacon]{ beacon->executeQuerySmooth(); });
}
~Tm() {
// Destructing objects living in other threads is undefined behavior
postCall(&m_dbThread, [this]{
if (m_db.thread() == QThread::currentThread())
m_db.moveToThread(thread());
});
}
...
};