bob2oneil
18th March 2011, 14:07
I am designing a multithreaded Qt application for embedded Linux that will consist of the main GUI thread along with
several long lived secondary threads. I am currently not deriving from QThread, based on the ongoing discusssion on Qt threading
best practices, and the lack of need to derive from QThread directly.
This is discussed in the following URLs on thread affinity.
http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/
http://labs.qt.nokia.com/2006/12/04/threading-without-the-headache/
I currently have implemented just two threads, the main GUI thread and a network thread, but this will be expanded as the design
matures to several secondary threads. The secondary network thread is long lived and responsible for dispatching received UDP
frames to corresponding secondary threads. In my current implementation, I create a packet on the heap, and then emit a Qt signal
with this packet to the main GUI thread. The Main GUI thread has a slot connection to receive the signal from the UDP secondary
thread (producer). The main GUI thread would be responsible for processing the content of the received packet and freeing the packet allocation
upon consumption. The network dispatcher would use a similar technique to provide data to the other secondary threads.
In my worker threads, I would like to effectively implement a pending queue, such that the secondary threads pend until they have a message
to process. The secondary threads would process the contents of the message, and then pend until another message is received.
My first attempt at this was to use a QWaitCondition to pend the Main GUI thead, where it would pend after transmitting content over the
network, and then wait on a response or a timeout.
Qt examples of the producer/consumer pattern uses global variables of a shared mutex and QWaitConditions to synchronize thread activity,
but this seems to break object oriented design principles.
I was using a QWaitCondition that would be signalled from the secondary thread upon the reception of a UDP frame. What I observed is that
the Main GUI thread did not receive the data signal from the UDP secondary thread while it was in the pending state on QWaitCondition. Perhaps
this makes sense as the event loop was not running in the main GUI thread, and without the event loop running, it would seem that the
thread is not receptive to a signal/event generated from a secondary thread. Assuming that the producer thread was to first awaken
the consumer thread via QWaitCondition.Wake(), it would seem there would be a race condition between awakening the thread, and then
sending out a signal containing the UDP packet.
It would appear that the GUI thread has to be running in its event loop to be able to receive a signal from a secondary thread, which under
the covers is implemented in Qt as an thread safe event.
I want to use the cross thread safe signal/slot mechanism to communicate between worker threads. This would seem to require that
the long lived secondary threads call the exec() function on their run() routines (when deriving from QThread). The event loop
exec() function is called by default in a QThread's run() routine.
Does exec() in a secondary thread actually pend the thread until activity is required such as in response to a slot being called or other signals received by the thread? I want to minimizing CPU utilization in the overall system such as if a secondary thread
has no data to process, it is in a pending state. Is this not possible when using a thread running an event loop and hence exec()?
Does anyone have an suggestions on implementing a multithreaded application based on signals and slots with long lived pending threads?
The following code snipnet illustrates my current secondary thread creation and signal/slot connections.
// 1. Create secondary threads (TODO: add others here)
QThread socketThread;
// 2. Connect objects that require threading to the thread start signal (TODO: add others)
Q_ASSERT(w.m_socket.connect(&socketThread, SIGNAL(started()), SLOT(start())) == true);
// 3. Connect object signals and slots together (TODO: add others)
Q_ASSERT(QObject::connect(&w.m_socket, SIGNAL(receiveGuiPacket(QByteArray *)), &w, SLOT(processPacket(QByteArray *))) == true);
// 4. Move objects that require threading to threads (TODO: add others)
// Tell Qt to ensure that event handlers, and by extension signals and slots, are called from the specified thread context
w.m_socket.moveToThread(&socketThread);
// 5. Start the threads (TODO: add others her)
socketThread.start();
// 6. Enter the main event loop and start event handling
int result = a.exec();
// 7. Tell all threads to shut down (TODO: add others here)
const int StopWait = 1000;
socketThread.quit();
// 8. Wait for all secondary threads to finish (TODO: add others here)
socketThread.wait(StopWait);
// Done
return result;
several long lived secondary threads. I am currently not deriving from QThread, based on the ongoing discusssion on Qt threading
best practices, and the lack of need to derive from QThread directly.
This is discussed in the following URLs on thread affinity.
http://labs.qt.nokia.com/2010/06/17/youre-doing-it-wrong/
http://labs.qt.nokia.com/2006/12/04/threading-without-the-headache/
I currently have implemented just two threads, the main GUI thread and a network thread, but this will be expanded as the design
matures to several secondary threads. The secondary network thread is long lived and responsible for dispatching received UDP
frames to corresponding secondary threads. In my current implementation, I create a packet on the heap, and then emit a Qt signal
with this packet to the main GUI thread. The Main GUI thread has a slot connection to receive the signal from the UDP secondary
thread (producer). The main GUI thread would be responsible for processing the content of the received packet and freeing the packet allocation
upon consumption. The network dispatcher would use a similar technique to provide data to the other secondary threads.
In my worker threads, I would like to effectively implement a pending queue, such that the secondary threads pend until they have a message
to process. The secondary threads would process the contents of the message, and then pend until another message is received.
My first attempt at this was to use a QWaitCondition to pend the Main GUI thead, where it would pend after transmitting content over the
network, and then wait on a response or a timeout.
Qt examples of the producer/consumer pattern uses global variables of a shared mutex and QWaitConditions to synchronize thread activity,
but this seems to break object oriented design principles.
I was using a QWaitCondition that would be signalled from the secondary thread upon the reception of a UDP frame. What I observed is that
the Main GUI thread did not receive the data signal from the UDP secondary thread while it was in the pending state on QWaitCondition. Perhaps
this makes sense as the event loop was not running in the main GUI thread, and without the event loop running, it would seem that the
thread is not receptive to a signal/event generated from a secondary thread. Assuming that the producer thread was to first awaken
the consumer thread via QWaitCondition.Wake(), it would seem there would be a race condition between awakening the thread, and then
sending out a signal containing the UDP packet.
It would appear that the GUI thread has to be running in its event loop to be able to receive a signal from a secondary thread, which under
the covers is implemented in Qt as an thread safe event.
I want to use the cross thread safe signal/slot mechanism to communicate between worker threads. This would seem to require that
the long lived secondary threads call the exec() function on their run() routines (when deriving from QThread). The event loop
exec() function is called by default in a QThread's run() routine.
Does exec() in a secondary thread actually pend the thread until activity is required such as in response to a slot being called or other signals received by the thread? I want to minimizing CPU utilization in the overall system such as if a secondary thread
has no data to process, it is in a pending state. Is this not possible when using a thread running an event loop and hence exec()?
Does anyone have an suggestions on implementing a multithreaded application based on signals and slots with long lived pending threads?
The following code snipnet illustrates my current secondary thread creation and signal/slot connections.
// 1. Create secondary threads (TODO: add others here)
QThread socketThread;
// 2. Connect objects that require threading to the thread start signal (TODO: add others)
Q_ASSERT(w.m_socket.connect(&socketThread, SIGNAL(started()), SLOT(start())) == true);
// 3. Connect object signals and slots together (TODO: add others)
Q_ASSERT(QObject::connect(&w.m_socket, SIGNAL(receiveGuiPacket(QByteArray *)), &w, SLOT(processPacket(QByteArray *))) == true);
// 4. Move objects that require threading to threads (TODO: add others)
// Tell Qt to ensure that event handlers, and by extension signals and slots, are called from the specified thread context
w.m_socket.moveToThread(&socketThread);
// 5. Start the threads (TODO: add others her)
socketThread.start();
// 6. Enter the main event loop and start event handling
int result = a.exec();
// 7. Tell all threads to shut down (TODO: add others here)
const int StopWait = 1000;
socketThread.quit();
// 8. Wait for all secondary threads to finish (TODO: add others here)
socketThread.wait(StopWait);
// Done
return result;