问题
I'm having issues with QT threading somehow stalling the main GUI.
To answer the first question... yes, QThread is not subclassed, and is done the "right way".
The thread is run long-term (not a "do x then exit"), and it's main loop does have delays/sleeps.
The threads main purpose is to collect data from elsewhere, compose an image, and transmit that image over a COM port.
There are coding areas that i think could be creating the problem.
Im using my own "sleep" function as msleep is not available (private to QThread only). Can anyone see anything wrong with the code below?
void SendImageJob::tSleep(int ms)
{
QElapsedTimer timer;
timer.start();
while ((timer.elapsed() < ms) && !abort)
{
QCoreApplication::processEvents();
}
}
The second thing that may be an issue is in the amount of data being sent to/from the thread via signals/slots. The image data being sent via a signal is about 16KB in size, and is sent at 15fps. Is this too much for queued connection signals to handle?
I would check the Qt signal queue length, but i cant find out how this can be done?
Thanks in advance!
回答1:
There's nothing wrong with your sleep
code except that you don't need to use it at all. In Qt 5, thread's sleep
methods aren't private anymore. In Qt 4, you can trivially work around it. You need to use this safe QThread
wrapper anyway for true RAII goodness, so you may as well expose the static sleep methods in the same class:
class Thread : public QThread {
using QThread::run; // final, no subclassing
public:
Thread(QObject * parent = 0) : QThread(parent) {}
~Thread() { quit(); wait(); }
using QThread::sleep;
using QThread::msleep;
using QThread::usleep;
};
Part of your problem may be that you're using a blocking QObject
, and that's a slightly backward design. Unless you're stuck using blocking APIs (say a broken database interface library), your object should perform satisfactorily on any thread, including the GUI thread. You only move it to a separate thread to decrease the GUI thread's latency.
One way to sanely implement it is to leverage asynchrony:
class Worker : public QObject {
Q_OBJECT
typedef void (Worker::*State)();
QBasicTimer m_timer;
State m_nextState;
void timerEvent(QTimerEvent * ev) {
if (ev->timerId() != m_timer.timerId()) return;
m_timer.stop();
(this->*m_nextState)();
}
void wait(State state, int ms) {
m_nestState = state;
m_timer.start(this, ms);
}
void state1() {
...
wait(&Worker::state2, 100); // wait 100ms and continue in state2
}
void state2() {
...
}
public:
Worker(QObject * parent = 0) : QObject(parent), m_nextState(&Worker::state1) {
m_timer.start(this, 0);
}
};
Such code becomes cleaner if you can use Qt 5 and C++11 - lambdas to the rescue. You can also investigate the use of QStateMachine
.
Pushing 16kbytes at 16fps is nothing.
回答2:
Changing your application's viewpoint could also help. Instead of 'doing something in a thread', you can create your DataSource
class and move it to a QThread
. This data source should be responsible to create a frame (through an onCreateFrame
slot), and can be triggered by a QTimer
15 times per second.
Let Qt do the waiting :)
Brief and only to show a hint of a design:
QApplication app;
auto datasource = new DataSource(&app);
auto comsender = new ComPort("COM1", &app); // or whatever your class
QTimer fpstimer;
fpstimer.setInterval(1000/15);
check_for_success(QObject::connect(
datasource, DataSource::data,
comsender, ComPort::sendData));
check_for_success(QObject::connect(
&fpstimer, QTimer::timeout,
datasource, DataSource::onCreateFrame));
// make sure everything runs on it's own thread
datasource->moveToThread(new QThread(&app));
comsender->moveToThread(new QThread(&app));
app.exec();
Hope this might help.
来源:https://stackoverflow.com/questions/24864594/qt-threading-issues-something-is-stalling-gui-response