Initialization in a new thread.
-
I am doing threading right, and was wondering how to declare heap variables in my new thread.
Now the way I do it is, I connect two slots "slot_1" and "slot_2" to execute when the new thread starts (connecting them in that order).
In slot_1, I perform initialization and declare my heap variables.
In slot_2, I do my work.My question: Am I assured that all variables will come into existence by the time slot_2 is invoked?
(I know... silly lab rat! Do the initialization first, in the same slot, and then proceed to doing the work! No need for 2 slots!... Well, yes, I need it. My slot_2 is invoked by another signal, repeatedly, so I can't initialize anything in it.
Thanks -
Hi, and welcome to the Qt Dev Net!
Can you show us some code on how you have set up your thread? What class does "slot_1" and "slot_2" belong to?
-
Thank you.
Tying the question above to my example code below, slot_1 is "doInit", and slot_2 is "process". You will see that both slots belong to the "Dataserver".
Here is my main:
@int main(int argc, char *argv[])
{
QApplication a(argc, argv);MainWindow w; w.show(); // create the dataserver thread: QThread * dsThread = new QThread; // create the dataserver worker object: Dataserver dataserver; dataserver.doConnections(*dsThread, &w); // move the worker object onto the new thread: dataserver.moveToThread(dsThread); dsThread->start(); return a.exec();
}@
Now the Dataserver class header file:
@class Dataserver : public QObject
{
Q_OBJECT
public:
explicit Dataserver(QObject *parent = 0);
void doConnections(QThread &cThread, MainWindow *);
~Dataserver();signals:
void finished();public slots:
void process();
void doInit();private:
StatusCollector * statusCollector; // token pointer to heap object
};@and the Dataserver class .cpp file:
@void Dataserver::doConnections(QThread &cThread, MainWindow *win)
{
// When the thread starts, do one time initialization:
connect(&cThread, SIGNAL(started()), this, SLOT(doInit()));
// When the thread starts, run:
connect(&cThread, SIGNAL(started()), this, SLOT(process()));
// On closing down, wait for thread to complete
connect(&cThread, SIGNAL(finished()), &cThread, SLOT(deleteLater()));
}void Dataserver::doInit()
{
// create heap variables
statusCollector = new StatusCollector(this);
// and more...
}void Dataserver::process()
{
// This slot is started on thread start, and waits on a precise real-time
// clock interrupt; When the real-time interrupt happens, this wakes up and
// executes a number of functions, and returns to waiting on the
// real time clock interrupt.
}
@
To recap, on the start of dsThread, two slots are invoked: the "doInit" slot and the "process" slot, both in the worker dataserver object.
So my question is, will "doInit" be done (i.e. will the statusCollector come into existence) by the time my slot "process" is invoked? -
slots should be invoked in the same order they have been defined, but the question here is do you really need to connect 2 different slots to the same signal? :D
you could call the doInit() function once in your process slot (I know you said that is not possible, but why not?) or you could use an intermediate slot to call both slots form there, or call the process() slot from the doInit() slot, that is all the same result to me, is it not?
Anyway it seems a little weird how you use the QThread with the Dataserver is not derived from QThread or some easier solution, just wondering :)
-
I agree, the simpler solution is a function call to "doInit" from the "process" slot, once.
About the last paragraph and the qthread, I have read that subclassing qthreads is not the correct way to do it; So I instantiate a worker object (dataserver) and hoist it onto the new thread, with moveToThread.
But I always wondered how to instantiate new heap parameters in objects belonging to the new thread, since you shouldn't invoke the objects' constructors form your main thread... -
[quote author="Xander84" date="1396981794"]slots should be invoked in the same order they have been defined[/quote]Correct. From the "Signals and Slots documentation":http://qt-project.org/doc/qt-5/signalsandslots.html:
"If several slots are connected to one signal, the slots will be executed one after the other, in the order they have been connected, when the signal is emitted."
[quote author="Hibou" date="1396982720"]I agree, the simpler solution is a function call to "doInit" from the "process" slot, once.[/quote]Your original solution is fine.
Other thoughts: Do you need to run process() when the thread starts? Could you just run doInit() when the thread starts, and let your other signal trigger process()?
[quote author="Xander84" date="1396981794"]Anyway it seems a little weird how you use the QThread with the Dataserver is not derived from QThread or some easier solution, just wondering :)[/quote]No, deriving from QThread is not a good solution here. Hibou explained in his original post that his process() slot will be invoked repeatedly by another signal. You should use a worker object for that.
[quote]About the last paragraph and the qthread, I have read that subclassing qthreads is not the correct way to do it; So I instantiate a worker object (dataserver) and hoist it onto the new thread, with moveToThread.[/quote]Subclassing QThread and using a worker object are two different ways to use QThread -- you should choose the solution based on your use case. "Multithreading Technologies in Qt":http://qt-project.org/doc/qt-5/threads-technologies.html and the "QThread documentation":http://qt-project.org/doc/qt-5/qthread.html explain how to choose.
In your case, creating a worker object was the correct choice, because you are using signals to trigger functions (slots) in your new thread.
[quote]But I always wondered how to instantiate new heap parameters in objects belonging to the new thread, since you shouldn’t invoke the objects’ constructors form your main thread…[/quote]It depends on the nature of your heap objects. If they can be moved to another thread, then just allocate them in your Dataserver constructor, and make Dataserver their parent. When you move Dataserver to the new thread, its children will be moved too. See "QObject|Thread Affinity":http://qt-project.org/doc/qt-5/QObject.html#thread-affinity for details.
What kind of objects do you create in doInit()?
-
Thank you JKSH. Indeed, I could simply forgo the doInit() slot or function, if the new objects declared in the Dataserver constructor are children of the Dataserver object. The objects I create in doInit() are several, for instance an object to collect status from several TCP sockets, some objects to collect status from serial ports, etc. I greatly simplified the list and only kept one, but it could be long.
As for this question:bq. JKSH asked: "Other thoughts: Do you need to run process() when the thread starts? Could you just run doInit() when the thread starts, and let your other signal trigger process()?"
Yes, I believe I do. I am using a scheduler developed by another group, and for them to call my function repeatedly, at a very precise time, is to set it up when the thread starts. Then I wait for their signal. This function, by the way, is not "process" but yet another function to do the cyclic execution. So "process" simply sets things up, and coordinates with the scheduler.
Convoluted, but this way I leave the work of integrating the Timing IRIG-B board and interrupt vectoring to the low-level guys, and I stay at a fairly high level. -
Hmm... I'm not sure if QTcpSocket and QSerialPort can be moved after construction or not. I'll have a look when I get home.
-
Oh, I am not actually creating QTcpSocket's or QSerialPort's in this thread. I am sorry, I should have been clearer. They are in their own thread, and I interface to them via shared memory. I know there is no real need to place sockets in a different thread, but I do not want to process any incoming signals in my Dataserver thread (at least, after it gets going). The I/O thread, as I call it, will do all the asynchronous message receiving with slots.
Thanks for bringing this up.