3

I have a small chat application where I use a SQLite database to store all the conversations. I've noticed that the app freezes randomly, and I then have to minimize and maximize it to make it work again. I thought that the problem might be the SQLite selects / inserts that were causing the gui to freeze. I decided to try and move all the SQLite methods into a separate thread.

After doing so the app still freezes.

Some things that might be worth knowing:

  1. I use QTcpSocket directly in my MainWindow but it seems that there is no use in running the QTcpSocket in a separate thread?

  2. I have separated the SQLite methods into a new thread (see implementation below)

  3. I use 3 WebViews for displaying my chat messages, the entire application GUI is build with these WebViews

Does my code below really run in a separate thread? GUI still freezes.

My header file:

class dbThread : public QObject
{
     Q_OBJECT

public:
     dbThread(QObject* parent);

public slots:
     bool openDB(QString agentID);

signals:
     void clearPreviousHistory();

private:
     QSqlDatabase db;
     QHash<QString, QString> countries;
};

My cpp file:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
QThread* thread = new QThread(this);
dbtrad = new dbThread(this);
dbtrad->moveToThread(thread);

dbtrad->openDB(userID);

connect(dbtrad, SIGNAL(clearPreviousHistory()), this, SLOT(clearHistoryV()));
thread->start();

}



dbThread::dbThread(QObject * parent): QObject(parent) {
}

bool dbThread::openDB(QString agentID) {

    qDebug() << "OPEN DB FROM THREAD ";

    // Find QSLite driver
    db = QSqlDatabase::addDatabase("QSQLITE");

    // ......
}

This is how I call dbThread methods from my MainWindow:

dbtrad->getHistory(channelId);

Edit

New code:

// Start database thread
QThread* thread = new QThread(this);
dbtrad = new dbThread(this);
dbtrad->moveToThread(thread);

connect(this, SIGNAL(requestOpenDB(QString)), dbtrad, SLOT(openDB(QString)));
thread->start();

emit requestOpenDB(userID);
Alosyius
  • 8,771
  • 26
  • 76
  • 120
  • show code where u call dbthread functions. Also do you call them from main thread ? you may be misinterpreting what `moveToThread` does. – Abhishek Bansal May 22 '14 at 07:50
  • See my edit, I've posted how I call db methods from my main thread – Alosyius May 22 '14 at 07:51
  • [QThread::currentThreadId () **Warning:** The handle returned by this function is used for internal purposes and should not be used in any application code. **Warning:** On Windows, the returned value is a pseudo-handle for the current thread](http://qt-project.org/doc/qt-4.8/qthread.html#currentThreadId) – UmNyobe May 22 '14 at 08:18
  • The true question is. Does it still freeze? – UmNyobe May 22 '14 at 08:20
  • Before I do all updates (many methods) I wanted to make sure that its really running in a different thread. Is there another way to verify it? – Alosyius May 22 '14 at 08:20
  • 1
    @Alosyius give the new thread a name and call `qDebug()<<"function called from: "<objectName();` – ratchet freak May 22 '14 at 08:52

4 Answers4

2

dbtrad->openDB(userID); will execute like any normal function (Why should it?), in the GUI thread.

moveToThread allow you to execute slots called using signals in a separate thread.

If you want to execute openDB in the thread you can trigger its execution using

 connect (thread, SIGNAL(started()), dbtrad, SLOT(openDBWithUIDAlreadySet()))

or

 connect (this, SIGNAL(requestOpenDB(int)), dbtrad, SLOT(openDB(int)))

You need to use existing or additional signals. Qthread::start() emit the signal started(). You can also define

MainWindow{

signals:
    void  requestOpenDB(int);
    void queryHistory(int channelid);
}

and emit the signals manually using

emit requestOpenDB(userID);    //for openDB
emit queryHistory(channelId);  // for getHistory

the responses from the dbThread object also need to be given using a signal which is connected to a slot. Like a notification.

UmNyobe
  • 22,539
  • 9
  • 61
  • 90
  • Ahhh I see! I'll update all my db calls with connects and see if that works – Alosyius May 22 '14 at 07:55
  • How would I make this call with a connect instead? `dbtrad->getHistory(channelId);` – Alosyius May 22 '14 at 07:56
  • I tried printing out: `qDebug() << this->thread()->currentThreadId();` both from inside MainWindow and inside the thread and I get same result: `0x7fff7300f180` – Alosyius May 22 '14 at 08:06
  • with the code you posted you are still inside the main GUI thread. You need to show the new code. – UmNyobe May 22 '14 at 08:11
0
  1. QTcpSocketdoes indeed not need to be in a separated thread.

  2. as long as all the database access is done from that thread where the database was created it should also be no problem

  3. And now to the fun part: i think you create the database in the main thread ... by calling dbtrad->openDB(userId)

Zaiborg
  • 2,492
  • 19
  • 28
0

Yes so qt moveToThread() does not do what you are expecting it to do. The function that you are calling from your main thread will get executed in your main thread only. That database access is causing GUI freezes.

moveToThread only moves "event processing" in a seperate thread. Which means any slots of dbThread which are connected using Qt::QueuedConnectionwill get executed in new thread.

Following way will execute getHistory() method in your main ui thread only. You need to create a signal in main thread and make getHistory() a slot of dbThread class. Then connect both.

Abhishek Bansal
  • 5,197
  • 4
  • 40
  • 69
0

Reading documentation AND logs is essential!!!
In log you have a warning that YOU CAN"T MOVE TO THREAD IF OBJECT HAVE A PARENT. Also documentation clearly says that:

Changes the thread affinity for this object and its children. The object cannot be moved if it has a parent. Event processing will continue in the targetThread.


Proper way to fix it:
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    thread = new QThread(this);
    dbtrad = new dbThread(); // NO PARENT
    dbtrad->moveToThread(thread);

    // run object method in thread assigned to this object:
    QMetaObject::invokeMethod(dbtrad, "openDB", Qt::QueuedConnection, Q_ARG(QString, userID));

    connect(dbtrad, SIGNAL(clearPreviousHistory()), this, SLOT(clearHistoryV()));
    thread->start();
}

MainWindow::~MainWindow()
{
    dbtrad->deleteLater();
    thread->quit();
    thread->wait(5000); // wait max 5 seconds to terminate thread
}
Marek R
  • 32,568
  • 6
  • 55
  • 140
  • How can this be fixed? Not sure I know what to change to make it to work – Alosyius May 22 '14 at 09:00
  • do not set parent, see edit. You should also connect `dbtrad->openDB` to some signal from main window and pass `userID` this way, or do it like I did it by using meta data invoke. – Marek R May 22 '14 at 09:32