Мое решение - использовать QtConcurrent
с пользовательским пулом потоков, который не уничтожает его потоки. И в контексте каждого потока я создаю выделенное QSqlDatabase
соединение с именем этого потока в качестве имени соединения, так что каждый поток получает одно и то же соединение каждый раз, когда ему нужно общаться с базой данных.
Установка:
mThreadPool = new QThreadPool(this);
// keep threads indefinitely so we don't loose QSqlDatabase connections:
mThreadPool->setExpiryTimeout(-1);
mThreadPool->setMaxThreadCount(10); /* equivalent to 10 connections */
qDebug() << "Maximum connection count is "
<< mThreadPool->maxThreadCount();
Destructor:
// remove the runnables that are not yet started
mThreadPool->clear();
// wait for running threads to finish (blocks)
delete mThreadPool;
Пример реализации API, возвращающий будущее, которое можно использовать для получения данных из базы данных, когда они станут доступны:
QFuture<QList<ArticleCategory> *>
DatabaseService::fetchAllArticleCategories() const
{
return QtConcurrent::run(mThreadPool,
&DatabaseService::fetchAllArticleCategoriesWorker, mConnParameters);
}
Обратите внимание, что мое решение не управляет объектами, которые оно создает.
Вызывающий код должен управлять этой памятью (QList
, возвращенный выше).
Рабочая функция сопутствующего потока:
QList<ArticleCategory> *DatabaseService::fetchAllArticleCategoriesWorker(const DatabaseConnectionParameters &dbconparams)
{
try {
setupThread(dbconparams);
} catch (exceptions::DatabaseServiceGeneralException &e) {
qDebug() << e.getMessage();
return nullptr;
}
QString threadName = QThread::currentThread()->objectName();
QSqlDatabase db = QSqlDatabase::database(threadName, false);
if (db.isValid() && db.open()) {
QSqlQuery q(db);
q.setForwardOnly(true);
// ...
}
// else return nullptr
// ...
}
Если вы заметили, setupThread
всегда вызывается в начале рабочего процесса, который в основном подготавливает соединение с базой данных для вызывающего потока:
void DatabaseService::setupThread(const DatabaseConnectionParameters &connParams)
{
utilities::initializeThreadName(); // just sets a QObject name for this thread
auto thisThreadsName = QThread::currentThread()->objectName();
// check if this thread already has a connection to a database:
if (!QSqlDatabase::contains(thisThreadsName)) {
if (!utilities::openDatabaseConnection(thisThreadsName, connParams))
{
qDebug() << "Thread"
<< thisThreadsName
<< "could not create database connection:"
<< QSqlDatabase::database(thisThreadsName, false).lastError().text();
}
else
{
qDebug() << "Thread"
<< thisThreadsName
<< "successfully created a database connection.";
}
}
}