问题
I've a thread that read datas
class MyThread: QThread
{
...
}
void MyThread::run ()
{
uint8_t* buffer; // in my real code, it's a ring, so there is not read during write
// ...
while (true)
{
if (isInterruptionRequested())
return;
USB_READ(buffer);
emit newData(buffer);
}
}
In my UI Class I have:
connect(this, &UIClass::newData, m_thread, &MyThread::newData);
// ...
void newData(uint8_t* data)
{
// Process data
}
void UIClass::closeEvent(QCloseEvent *event)
{
disconnect(this, &UIClass::newData, m_thread, &MyThread::newData);
m_thread->requestInterruption();
m_thread->wait();
}
The problem with that if, when I click on "close", the thread is destroyed that cause the pointer data
to be invalid. The signal newData
is sometimes called that cause my function to work with invalid pointer and segfault. How to be sure that is not gonna happend ?
For now, I use a std::this_thread::sleep_for() with an arbitrary delay, it works, but I not find this very beautiful
That I have in my mind :
- disconnect the signal
- wait for the pendings signals to be executed
- exit
回答1:
The problem is that you send a pointer from one thread to another without ensuring the pointer stays valid.
You have multiple choices to solve this. Either use QSharedPointer (or similar utilities from the stl) to hold your data, doing so will ensure your pointer will remain valid (or provide you a way to detect when the pointer becomes invalid if you also use QWeakPointer). Or you could make use of QByteArray to pass the data, but this will make a copy.
Example 1
void MyThread::run ()
{
QSharedPointer<uint8_t> buffer (new uint8_t[N]()); // Do not delete[], QSharedPointer will handle it
...
emit newData(buffer);
}
void newData(QSharedPointer<uint8_t> data)
{
// data is always valid
// Process data
}
Example 2
void MyThread::run ()
{
QSharedPointer<uint8_t> buffer (new uint8_t[N]());
...
emit newData(buffer);
}
void newData(QWeakPointer<uint8_t> data)
{
// data might not be valid but we can check
QSharedPointer<uint8_t> buffer (data);
if (!buffer)
return;
// Process data
}
Example 3
void MyThread::run ()
{
uint8_t[N] buffer;
...
emit newData(QByteArray(buffer, size));
}
void newData(QByteArray data)
{
// data is valid
// Process data
}
回答2:
All you need to do is for the thread to outlive the user interface. That's rather easy:
class MyThread : public QThread
{
Q_OBJECT
RingBuffer buffer;
public:
void run() override;
~MyThread() {
requestInterruption();
quit();
wait();
}
Q_SIGNAL newData(RingBuffer *);
};
int main(int argc, char **argv) {
QApplication app{argc, argv};
MyThread thread;
thread.start();
UIClass ui;
connect(&thread, &MyThread::newData, &ui, &UIClass::newData);
return app.exec();
}
来源:https://stackoverflow.com/questions/41677990/waiting-slots-to-be-executed-before-quitting