I have a multithreaded Qt5 app written in C++ where each thread is writing to the same database.
I share the same database QSqlDatabase variable across threads, but each thread creates its own query. I am regularly getting crashes and I've captured this info at the time of a crash:
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]
First of all, can I make simultaneous MySQL queries in multiple threads? If so, do I need to protect SQL calls with a mutex? Or do I need to post more info....
UPDATE: I have a seperate DBManager thread which handles opening/closing the DB, and simple database writes. I did this because my DB often goes offline, and I don't want other threads to hang for up to 2 minutes while a db open fails. No I have more threads which do reports, and must retrieve substantial amounts of data from the db.
As noted below, sharing the db handle across threads is not permitted. So now perhaps this is more of a design question - what is the best way to handle this situation? I don't want each thread that does DB access to attempt it's own open and wait for 2 minutes in case the DB is offline
Read the documentation for the SQL module, in particular the threads section that states each connection can only be used within the thread that created it.
So adding a mutex is not good enough, you will have to either create a new connection object in each thread, or pass the required data to/from a dedicated thread that performs all DB queries.
I generally handle this problem by providing a factory method on my database manager class, something that looks similar to this (semi-pseudocode):
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;
}
As long as you make sure you create an initial connection (you can modify the factory to include that, or just create the initial connection in your DatabaseManager constructor), you should be able to replace most existing cases where you're using a QSqlDatabase with:
QSqlQuery query(DatabaseManager::database());
query.exec(<some sql>);
without worrying about which thread you're calling from. Hope that helps!
This is just mbroadst's excellent answer with minor changes. This code should be complete and directly compilable:
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;
}
Usage: Instead of QSqlDatabase::database(), use DatabaseManager::database()
Edited 2016-07-01: fixed bug with multiple connection names
Related
Disclaimer: I am relatively new to Qt and any type of programming that revolves around Threads and Networking. I have also adopted a lot of code from Qt Examples, API, and other online examples.
All code can be found on GitHub. This code is relatively as simple as it can get minus striping out GUI. I figure supplying it this way would help as well versus just pasting the code below.
I want to use and believe I need to use Threads as I need multiple clients send a request to the server, the server run some SQL code, then spit out the results back to the client (basically deriving a MySQL Server, but specific to what I am doing). Right now though, I am just working on learning the workings of it all.
With all that being said, as the Title states.. My client can connect to the server, the server sets up the thread, and will receive data (a String) through the readReady. After the data is read in, for right now I am just trying to echo it back to the client. It will do this, but only once. Then it spits out:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNativeSocketEngine(0x266cca92ea0), parent's thread is serverThread(0x266cca9ed60), current thread is QThread(0x266cac772e0)
I cannot send any further data to the server unless I have the client reconnect, then after the data is sent, it will do its job but then spit out the same error and cease functioning. I have tried quite a bit of different things, but cannot seem to fix the issue. I even tried setting up a SIGNAL/SLOT for this as suggested in API:
It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread.
Anyway, any help would be greatly appreciated! My Code is below..
Server
ServerThread.cpp
// Project
#include "ServerDialog.h"
#include "ServerThread.h"
ServerThread::ServerThread(qintptr _socketDiscriptor, QObject *parent /*= 0*/)
: QThread(parent)
{
socketDiscriptor = _socketDiscriptor;
}
void ServerThread::run()
{
emit threadStarted(socketDiscriptor);
// Start Thread
clientSocket = new QTcpSocket;
// Set SocketDisc
if (!clientSocket->setSocketDescriptor(socketDiscriptor))
{
emit error(clientSocket->error());
return;
}
// Connect Socket and Signal
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(clientSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
//// Loop Thread to Stay Alive for Signals and Slots
exec();
}
void ServerThread::readyRead()
{
QDataStream in(clientSocket);
in.setVersion(QDataStream::Qt_5_7);
in.startTransaction();
QString dataReceived;
in >> dataReceived;
if (!in.commitTransaction())
{
emit readyReadError(socketDiscriptor);
return;
}
emit readyReadMessage(socketDiscriptor, dataReceived);
echoData(dataReceived);
}
void ServerThread::disconnected()
{
emit threadStopped(socketDiscriptor);
clientSocket->disconnect();
clientSocket->deleteLater();
this->exit(0);
}
void ServerThread::echoData(QString &data)
{
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_7);
out << data;
clientSocket->write(block);
}
So in ServerThread.cpp when echoData is called, that is when the error shows up and the Socket ceases functioning.
Any and all help will be appreciated. I know there are a few other posts regarding "Cannot create children for..." in regards to Threads. But I did not find any of them helpful. The one thing that I did find interesting but did not understand was maybe using moveToThread() but a lot of mixed comments on that.
I learn best through code examples along with explanation versus just an explanation or pointer to API. Thank you!
Most of Qt network functions are asynchronous; they do not block the calling thread. There is no need to mess up with threads if you are using QTcpSockets. In fact, creating a thread for every socket is an overkill, since that thread will spend most of its time just waiting for some network operation to finish. Here is how I would implement a single-threaded echo server in Qt:
#include <QtNetwork>
#include <QtCore>
//separate class for the protocol's implementation
class EchoSocket : public QTcpSocket{
Q_OBJECT
public:
explicit EchoSocket(QObject* parent=nullptr):QTcpSocket(parent){
connect(this, &EchoSocket::readyRead, this, &EchoSocket::EchoBack);
connect(this, &EchoSocket::disconnected, this, &EchoSocket::deleteLater);
}
~EchoSocket() = default;
Q_SLOT void EchoBack(){
QByteArray receivedByteArray= readAll();
write(receivedByteArray);
disconnectFromHost();
}
};
class EchoServer : public QTcpServer{
public:
explicit EchoServer(QObject* parent= nullptr):QTcpServer(parent){}
~EchoServer() = default;
//override incomingConnection() and nextPendingConnection()
//to make them deal with EchoSockets instead of QTcpSockets
void incomingConnection(qintptr socketDescriptor){
EchoSocket* socket= new EchoSocket(this);
socket->setSocketDescriptor(socketDescriptor);
addPendingConnection(qobject_cast<QTcpSocket*>(socket));
}
EchoSocket* nextPendingConnection(){
QTcpSocket* ts= QTcpServer::nextPendingConnection();
return qobject_cast<EchoSocket*>(ts);
}
};
int main(int argc, char* argv[]){
QCoreApplication a(argc, argv);
EchoServer echoServer;
echoServer.listen(QHostAddress::Any, 9999);
QObject::connect(&echoServer, &EchoServer::newConnection, [&](){
EchoSocket* socket= echoServer.nextPendingConnection();
qDebug() << "Got new connection from: " << socket->peerAddress().toString();
});
return a.exec();
}
#include "main.moc"
Notes:
This server has the ability to handle more than one client at the same time, since there is no blocking. The thread will just respond to the event that happens with the appropriate action; So, if that event was a new connection, it will create a new EchoSocket object to handle it and prints a statement out to qDebug(), and if that event was receiving something on a previously created socket, the same thread will echo received data back and close the connection. It will never block on a single connection waiting for data to arrive nor it will block waiting for a new connection to arrive.
Since you mention using some SQL queries in response for some connections later in your project. Please avoid threading since an SQL database connection in Qt can be used only from the thread that created it, see docs here. So, You'll have to either create a new database connection for each thread (and thus for each connection) in your application (and this is beyond just overkill), or switch later to a single threaded design.
In this section, I am explaining why threading does not work for you the way you are doing it:
You should not be declaring slots in your QThread subclass, Instead, use worker QObjects and move them to QThreads as needed.
The quote you have provided in your question is the exact explanation for why you get this warning. The ServerThread instance you create will be living in the main thread (or whatever thread that created it). Now let's consider this line from your code:
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
The signal readyRead() will be emitted from the current ServerThread instance (since the clientSocket object that emits it lives there), However, the receiver object is the current ServerThread instance, But that lives in the main thread. Here is what the documentation says:
If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used.
Now, the main point of Qt::QueuedConnection is executing the slot in the receiver object's thread. This means that, your slots ServerThread::readyRead() and ServerThread::disconnected will get executed in the main thread. This is most likely not what you meant to do, since you'll end up accessing clientSocket from the main thread. After that, any call on clientSocket that results in child QObjects being created will result in the warning you get (you can see that QTcpSocket::write() does this here).
Mixed comments of movetothread are linked mostly to usage of it to move thread object to itself.
The quote hints that the members of QThread aren't designed to be called from worker. Strictly proper way to call signal would be by using worker object model, that was shown in Qt examples and explained a few times on QT-related blogs:
class Worker : public QObject
{
Q_OBJECT
private slots:
void onTimeout()
{
qDebug()<<"Worker::onTimeout get called from?: "<<QThread::currentThreadId();
}
};
class Thread : public QThread
{
Q_OBJECT
private:
void run()
{
qDebug()<<"From work thread: "<<currentThreadId();
QTimer timer;
Worker worker;
connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout()));
timer.start(1000);
exec();
}
};
worker constructed inside run() is "property" of the thread it created, so figuratively speaking, it is slaved to its context. The same effect maybe achieved if you create worker in other thread, then move it to this thread before connection was made. When you connect signal to slot of the QThread itself, you connect child thread to thread it was created by.
Use of
connect(&timer, SIGNAL(timeout()), this, SLOT(onTimeout()), Qt::DirectConnection);
or creating connection from your thread sometimes seems to achieve proper result, but not in this case, where you try use objects constructed in different threads together. Calling moveToThread(this) in constructor is a thing not recommended to do.
In Qt-creator, I created SQLite database in a class called databaseManager, as follow:
QString DatabaseManager::open_db()
{
QSqlDatabase db;
QString path = "/Users/me/Documents/workspace/Muasaa/";
db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(path+"Database v.1");
if (db.open()){
return "Database is created, open, and ready ...";
} else {
return db.lastError().text();
}
}
Then I define the following in the header file of the MainWindow class:
Public:
DatabaseManager *db_manager;
In the source file, I call it as follow:
db_manager->open_db();
which creates and open the database.
However, I would like to use a reference to same database to use it in many functions in the MainWindow source file. How can I do that ?!
Move the QSqlDatabase db variable into your class header, and add a method for getting it. As long as you create an instance of your databaseManager class and maintain a pointer to it in MainWindow you'll be able to retrieve it.
QSqlDatabase::database() is a static function that returns a QSqlDatabase.
If you have more than one database, you have to provide a connection name in addDatabase() and in database()
Maybe a design solution may help?
Whatever your goal is, consider turning your DatabaseManager class into a Singleton. This way you'll be able to use your manager in all the gui classes. Something like DatabaseManager::instance()->your_method()
Advantages:
You are sure the connections are managed the right way
Less oportunities for build problems
By the way, I'm not sure, but your program crash may be the result of using your db_manager pointer before it was initialized, in a slot maybe, or (more probable) it's an internal connection error. Have you checked the same connection attributes in a minimum possible example?
Here's the problem: let's say we have a lot of threads (>1000) receiving data via some protocol and I want to write this data in database (sql server).
For database connection, we use QSqlDatabase. According to the documentation of QSqlDatabase:
A connection can only be used from within the thread that created it. Moving connections between threads or creating queries from a different thread is not supported.
Considering we cannot create 1000 connections (which can cause performance problems), what are the options here?
What comes to mind is to create a separate thread for database writings handling.
But how to run this thread in background and wait for signals from data receiving threads? What are the other approaches that can be used in this situation?
i used a database connection as a global logger, so you might be able to adapt it to fit your needs.
the skeleton looks like:
//Logger.h
class Logger : public QObject
{
Q_OBJECT
...
public:
void log(const QString& msg):
private:
LoggerWorker* w;
QThread t;
}
// Logger.cpp
Logger::Logger(QObject* p)
:QObject(p)
{
w = new LoggerWorker;
w->moveToThread(&t);
connect(&t, SIGNAL(started()), w, SLOT(init()));
t.start();
}
...
void
Logger::log(const QString& msg)
{
QMetaObject::invokeMethod(w, "log", Q_ARG(QString, msg));
}
// LoggerWorker.h
class LoggerWorker : public QObject
{
Q_OBJECT
...
public slots:
void init()
{
// create database and connect to it
}
void log(const QString& msg)
{
// insert to database
}
private:
QSqlDatabase db;
}
note: code above might contain nuts and bugs
cheers
The best option is to have a class for working with database which resides in separate thread.You can perform all the database related operations in relevant slots. Different threads can interact with object of the class through connecting their signals to it's slots. Since the emitter and receiver exist in different threads, you will have a queued connection type. So different signals that are emitted from different threads would be queued in the database class to be processed.
I have a Qt GUI class preferencesWindow that, obviously, is responsible for handling the user preferences. I have some fields that manage the connection to a database server. When a field is left, dbsChanged() method is called. Below is some code I managed to write:
void preferencesWindow::dbsChanged() {
QFuture<QStringList> loader = run(this, &preferencesWindow::get_databases);
QStringList databases = loader.result();
if (databases.length()) {
this->ui.database->show();
this->ui.nodb_label->hide();
this->ui.database->clear();
this->ui.database->addItems(databases);
this->ui.okButton->setDisabled(false);
this->ui.validationStatus->setPixmap(QPixmap(":/icon/tick.png"));
} else {
this->ui.database->hide();
this->ui.nodb_label->show();
this->ui.okButton->setDisabled(true);
this->ui.validationStatus->setPixmap(QPixmap(":/icon/error.png"));
}
}
QStringList preferencesWindow::get_databases() {
QSqlDatabase test_connection;
if (QSqlDatabase::contains("PREFEREMCES_LIVE_TEST_CONNECTION"))
test_connection = QSqlDatabase::database("PREFEREMCES_LIVE_TEST_CONNECTION");
else test_connection = QSqlDatabase::addDatabase("QMYSQL", "PREFEREMCES_LIVE_TEST_CONNECTION");
test_connection.setHostName(this->ui.serverAddress->text());
test_connection.setUserName(this->ui.username->text());
test_connection.setPassword(this->ui.password->text());
test_connection.setDatabaseName(this->ui.database->currentText());
test_connection.setPort(this->ui.serverPort->value());
test_connection.open();
qDebug() << "Error: " << test_connection.lastError();
QSqlQuery show_databases = test_connection.exec("show databases");
QStringList databases;
while (show_databases.next()) {
databases.append(show_databases.value(0).toString());
}
QSqlDatabase::removeDatabase("PREFERENCES_LIVE_TEST_CONNECTION");
return databases;
}
Since get_databases can take a long time, I thought that putting in on a separate thread as you can see in these two lines:
QFuture<QStringList> loader = run(this, &preferencesWindow::get_databases);
QStringList databases = loader.result();
could solve the problem. It runs on a separate thread, but it still freezes the GUI (while working).
How should I rewrite this entire process? I though of some solutions, but I am not really sure about their performance, and I don't want to work uselessly...
It freezes the GUI because even though the get_databases call is in a separate thread, you still wait for the results which causes the freeze.
I don't know how to do it in Qt, but the normal thing would be to open a dialog saying "please wait" or something with a cancel button, and have the worker thread send a signal to the parent (GUI) thread when done.
The QFuture will wait until the thread sets the result when your call loader.result(). You have to wait for that value later.
I guess you could store the future object as member of preferencesWindow and send yourself a signal, when finishing get_databases. So you give your application time to process other events during this wait time.
You can use QFutureWatcher to monitor that status of the QFuture object, like written in the documentation:
// Instantiate the objects and connect to the finished signal.
MyClass myObject;
QFutureWatcher<int> watcher;
connect(&watcher, SIGNAL(finished()), &myObject, SLOT(handleFinished()));
// Start the computation.
QFuture<int> future = QtConcurrent::run(...);
watcher.setFuture(future);
I am using the Qt libraries in a C++ project but I have a design question: where should a database be declared? I would prefer not to declare global variables.
Currently I am dealing with this problem in this way. I have a mainwindow and I have declared the DB in there so I perform the queries in the main window and pass the results to the dialogs using different signals and slots.
I start the DB when the main window starts and close it when the window has been closed. I don't know if this is ok
Now I need the DB connection in another class as well so I can pass a reference to the DB or make the DB global
I don't like these solutions.. is there a standard pattern to deal with this situation?
edit:
My class now looks like:
class Database
{
public:
bool open(void);
bool close(void);
static Database* getDatabase(void);
// various methods like loadThisTable(), saveThisTable() etc
private:
Database(); // disable constructor
~Database(); // disable destructor
Database(const Database&); // disable copy constructor
Database& operator=(const Database&); // disable assignment
static Database* instance_; // database instance
QSqlDatabase qtDB; // qt db database
}
If I want I can add the add and remove methods but I have a single DB instance.
If you're using QSqlDatabase, you don't really need to make it a global variable. Just set up the connection when you first start your application, then use the static QSqlDatabase methods to access the connection when you need it in different modules.
Example
QSqlDatabase db; // set up the default connection
// alternative: set up a named connection
// QSqlDatabase db("conn-name");
// set the connection params and open the connection
// ... later on
QSqlDatabase db = QSqlDatabase::database(); // retrieve the default connection
// alternative: retrieve the named connection
// QSqlDatabase db = QSqlDatabase::database("conn-name");
From the docs:
QSqlDatabase is a value class. Changes made to a database connection via one instance of QSqlDatabase will affect other instances of QSqlDatabase that represent the same connection. Use cloneDatabase() to create an independent database connection based on an existing one.
Note: If you're application is multi-threaded, you have to be careful to only use a connection in the thread in which it was created.
You need a singleton pattern. It's a global class which have only one instance. Someone calls it antipattern (and sometimes it is), but it is the best way to handle resources like database connections. And dont forget that you can use QSqlDatabase QSqlDatabase::database ( const QString & connectionName = QLatin1String( defaultConnection ), bool open = true ) [static] method to get QSqlDatabase instance by name (name can be set via QSqlDatabase QSqlDatabase::addDatabase ( QSqlDriver * driver, const QString & connectionName = QLatin1String( defaultConnection ) ) [static] method) to avoid creating singleton just for storing QSqlDatabase instances.