views:

1829

answers:

5

What does it mean to move a object from one thread to another in Qt using moveToThread? Everything seems to work even before using moveToThread, which moves the object from one thread (GUI thread) to a another thread ( worked) and Qt:connect calls the appropriate slot on object.

Is there any difference because of where the object lives, GUI thread or the worker thread?

EDIT: I made a small program, but I don't understand how QThread works along with Signal and slot function, I would appreciate if you could explain what is the use of moveToThread with the example

#include <QtGui/QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QString>
#include "mythread.h"
//GUI calls a thread to do some job and sub update the text box once it is done
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    QHBoxLayout * pH = new QHBoxLayout(&w);
    QPushButton * pushButton = new QPushButton("asdad");
    QLineEdit * lineEdit = new QLineEdit("AAA");
    pH->addWidget(pushButton);
    pH->addWidget(lineEdit);
    w.setLayout(pH);
    w.show();
    MyThread thread;
    qDebug("Thread id %d",(int)QThread::currentThreadId());
    QObject::connect(pushButton,SIGNAL(clicked()),&thread,SLOT(callRun())) ;
    QObject::connect(&thread,SIGNAL(signalGUI(QString)),lineEdit,SLOT(setText(QString)));
    return a.exec();
}

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QMutex>

class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread();
public slots:
    void callRun();
    void run();
 signals:
    void signalGUI(QString);
private:
    QMutex mutex;

};

#endif // MYTHREAD_H


#include "mythread.h"
#include <QDebug>
#include <QString>
#include <QMutexLocker>

MyThread::MyThread()
{
}
 void MyThread::callRun()
 {

     qDebug("in thread");
    if(!isRunning())
     {
        this->start(LowestPriority);
        exec();
    }
    else
    {
        run();
    }

 }
 void MyThread::run()
 {
     QMutexLocker fn_scope(&mutex);
     static int a = 0;
    ++a;
     qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
     this->sleep(3);
     static QString number;
     QString temp;
     number += temp.setNum(a);
     emit signalGUI(number);
 }
+2  A: 

When moving an object between threads, you decide which event loop it belongs to. When making connections inside a thread, the signaling code directly calls each one of the slots (having to wait for them to finish). Signalling across thread boundaries places the signal call on the event loop, letting the slot's thread make the call to the slot when ready.

Making direct calls between threads requires you to make sure that your functions are reentrant. You must also make sure to protect your data using mutexes or semaphores and at the same time avoid race conditions.

In the article, I guess that the delay is due to the call being direct, i.e. not at all processed in the background (but I only skimmed the text).

e8johan
I think he was using queued connection even though the connection is between objects from same thread, so GUI waited for some other operations to complete before executing the slot for key press(slot need not be reentrant),and then freezes the GUI during slot execution also.Is my interpretation correct?
yesraaj
You are both close, but Qt does handle the transferring of data when you use the correct connections for signals and slots. I think the example he is looking at is the problem.
Adam W
@e8johan "make sure that your functions are reentrant" by function you meant slot right?
Passionate programmer
@e8johan "Signalling across thread boundaries places the signal call on the event loop" does that mean emit signal1; thread is different from the slot function's object's thread. Or emit object and receive objects are from different thread?
Passionate programmer
@Beginner: slots are functions. And the emits are placed on the currently running thread (be it GUI or worker). If there is a slot on a different thread that is connected to that signal it will run in the slot owner's thread when it is pulled off the event queue.
Adam W
@Beginner: @Adam put it nicely.
e8johan
@Adam @e8johan so the connection type is only determined at runtime that too based on where the emit is placed?
yesraaj
@yesraaj: Connection type is determined either at runtime or at compile time based on how you declare the connection. See: http://doc.trolltech.com/4.6/qt.html#ConnectionType-enum
Adam W
@Adam I am looking into the link u gave but I don't see abt how they make out the connection!!!
yesraaj
@yesraaj: http://doc.trolltech.com/4.6/qobject.html#connect
Adam W
+5  A: 

Take a look at Signals and slots across threads. If you always use signals and slots to communicate with the worker thread, Qt handles the moveToThread for you if it's needed and you used the correct connection.

Edit: I would guess the article's author was seeing his problem since he was calling start in the constructor before the thread was actually created. In other words, don't trust third-party code blindly.

Edit: In response to your comment, look at the Mandelbrot example, under the MandelbrotWidget Class Implementation header:

With queued connections, Qt must store a copy of the arguments that were passed to the signal so that it can pass them to the slot later on. Qt knows how to take of copy of many C++ and Qt types, but QImage isn't one of them. We must therefore call the template function qRegisterMetaType() before we can use QImage as parameter in queued connections.

I believe this is slightly outdated, here are the valid meta types. Since signals and slots across threads use queued connections, you should not have to do the moveToThread calls in most cases.

Edit: I will try to explain things with a similar example:

mythread.h:

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QMutex>

class MyThread : public QThread
{
   Q_OBJECT

protected:
   virtual void run();

signals:
   void signalGUI(QString);
};

#endif // MYTHREAD_H

mythread.cpp:

#include "mythread.h"
#include <QString>

void MyThread::run()
{
   qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
   static int run = 0;
   QString temp = QString("Run: %1").arg(run++);
   qDebug("String address inside run %p", &temp);
   emit signalGUI(temp);
}

mylineedit.h

#ifndef MYLINEEDIT_H
#define MYLINEEDIT_H

#include <QLineEdit>

class MyLineEdit : public QLineEdit
{
Q_OBJECT
public:
    explicit MyLineEdit(QWidget *parent = 0);

public slots:
    void setText(const QString &string);

};

#endif // MYLINEEDIT_H

mylineedit.cpp

#include "mylineedit.h"
#include <QThread>

MyLineEdit::MyLineEdit(QWidget *parent) :
    QLineEdit(parent)
{
}

void MyLineEdit::setText(const QString &string)
{
   qDebug("Thread id inside setText %d",(int)QThread::currentThreadId());
   qDebug("String address inside setText %p\n", &string);
   QLineEdit::setText(string);
}

main.cpp:

#include <QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include "mythread.h"
#include "mylineedit.h"

//GUI calls a thread to do some job and sub update the text box once it is done
int main(int argc, char *argv[])
{
   QApplication a(argc, argv);
   QWidget w;
   QHBoxLayout * pH = new QHBoxLayout(&w);
   QPushButton * pushButton = new QPushButton("Run Thread", &w);
   MyLineEdit * lineEdit = new MyLineEdit(&w);

   pH->addWidget(pushButton);
   pH->addWidget(lineEdit);
   w.show();

   MyThread thread;
   qDebug("Thread id %d",(int)QThread::currentThreadId());
   QObject::connect(pushButton,SIGNAL(clicked()),&thread,SLOT(start())) ;
   QObject::connect(&thread,SIGNAL(signalGUI(const QString&)),lineEdit,SLOT(setText(const QString&)));
   return a.exec();
}

Sample output after clicking button:

Thread id 1088110320
Thread id inside run 1093176208
String address inside run 0x41288350
Thread id inside setText 1088110320
String address inside setText 0x974af58

As you can see, the run thread is different than the main GUI thread. Also, even though you pass a const reference to a QString, since it crosses thread boundaries it copies it. I strongly encourage you to read Threads and QObject.

Adam W
I did not see any documentation stating about Qt handling moveToThread, I read about Qt handles signal slot connection type based on where the object live.
Passionate programmer
Updated response, I may have over simplified a bit, but in most cases it will handle the work.
Adam W
@Adam What do you mean by " Qt handles the moveToThread for you if it's needed and you used the correct connection". As per the example `connect(` the `thread` and `this` are all objects from same thread but how the connection is queued connection?
Passionate programmer
@Beginner: The QImage you are passing in the signal/slot is copied to the slot's thread unless they are on the same thread when you use a queued (or auto) connection. And be careful, `thread` was created in the same thread as `this` but once you correctly start the thread, it runs another event loop.
Adam W
@Adam can you try explaining me with the code that I added :)
Passionate programmer
@Adam thanks, you alway answer some question that I did not ask but thanks for answers that I don't know, Please wait for couple of days let me see anyone else answers better than you.
Passionate programmer
I did not use your example because it did not show why moveToThread was not needed. You needed to see the address of the data passed through the signal/slot has changed to know why you don't need to move the data.
Adam W
@Adam: Thanks, my confusion was mainly where the start , exec, run functions should be used I think I am ok now
Passionate programmer
@Adam: Is it ok to call exec() again and again in a given thread?
Passionate programmer
@Adam: How come the signal slot mechanism work in SubThread with out exec; thread event loop
Passionate programmer
@Beginner: you don't call exec() on a thread. You call start() which in turn calls run() whose default implementation is exec(). exec(0 is protected. I assume calling run/start/exec multiple times results in nothing more that starting the thread the first time, but I don't know for sure. The signal slot mechanism works because of what I said above. Please read the QThread documentation: http://doc.trolltech.com/4.6/qthread.html#details
Adam W
@Adam W you just got 100 points. If you wish to add any more explanation please add, Thanks
Passionate programmer
The only thing I can think to add is that if you want events to run inside the thread, you need to call exec() from inside your run() function. Remember to avoid slots on a thread unless you use moveToThread(), signals don't require it.
Adam W
A: 

some objects only can be used on the owner thread. for example if you create and socket object in one thread and you want to send and recv data in another thread it is'nt possible. therefore one solution is to move your object from one thread to other and operate on it.

chezgi
+1  A: 
#include <QtGui/QApplication>
#include <QPushButton>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QString>
#include "mythread.h"
//GUI calls a thread to do some job and sub update the text box once it is done
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QWidget w;
    QHBoxLayout * pH = new QHBoxLayout(&w);
    QPushButton * pushButton = new QPushButton("asdad");
    QLineEdit * lineEdit = new QLineEdit("AAA");
    pH->addWidget(pushButton);
    pH->addWidget(lineEdit);
    w.setLayout(pH);
    w.show();
    MyThread thread;
    thread.moveToThread(&thread);
    thread.start();
    qDebug("Thread id %d",(int)QThread::currentThreadId());
    QObject::connect(pushButton,SIGNAL(clicked()),&thread,SLOT(callRun()),Qt::QueuedConnection) ;
    QObject::connect(&thread,SIGNAL(signalGUI(QString)),lineEdit,SLOT(setText(QString)),Qt::DirectConnection);
    return a.exec();
}

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QMutex>

class MyThread : public QThread
{
    Q_OBJECT
public:
    MyThread();
public slots:
    void callRun();
    void run();
 signals:
    void signalGUI(QString);
private:
    QMutex mutex;

};

#endif // MYTHREAD_H
#include "mythread.h"
#include <QDebug>
#include <QString>
#include <QMutexLocker>

MyThread::MyThread()
{
}
 void MyThread::callRun()
 {
     QMutexLocker fn_scope(&mutex);
     static int a = 0;
    ++a;
     qDebug("Thread id inside run %d",(int)QThread::currentThreadId());
     this->sleep(3);
     static QString number;
     QString temp;
     number += temp.setNum(a);
     emit signalGUI(number);

 }
 void MyThread::run()
 {
    exec();
 }

New thread object is created and the thread object is moved to the same thread. Signals are now across threads and connection type are both queue and it works as expected.

Passionate programmer
+2  A: 
  1. The QThread::start() method creates the thread and calls your run() implementation. If you want to handle events or received signals on the thread you have to call QThread::exec() inside your run() implementation. You should never call run() explicitly and you should never call exec() outside of run().

  2. The owner thread makes a difference only when a slot is connected to a signal with the connection type other than Qt::DirectConnection. Then Qt will ensure that the slot runs on the owner thread, but for that the owner thread must be running an event loop with QThread::exec(). In this case calling myObj.moveToThread(myThread) will ensure that myObj slots run on the thread myThread.

  3. The thread object belongs to the thread where it was created, not on the thread that it manages (and where the run method will run). So when you connect a signal to a thread object's slot, that slot will run in the thread where the thread object was created unless you call moveToThread().

Dan Berindei