Unsolved Assert failure: cannot send event to object owned by other thread; Debug only error in msvc using qthreadpool and qrunnable
-
hello dear qt people,
currently i am facing a problem with my msvc17 x64 compiler in debug mode with my qt gui application (qt version 5.12.2 on win10, c++17).
i am using an qtimer (in QWidgetClassInMainThread class) to call every ~20 to 50ms the method QWidgetClassInMainThread::startImageProcessingIteration().
that method should create an qrunnable derived object getting passed an opencv mat image in the constructor.this qrunnable will be passed later to an qthreadpool to do some calculations and drawings on the opencv image.
in the end the opencv image will be converted to an qimage in the reimplementedrun()
and an signal should emit the qimage back to the main thread where the qimage will be shown in gui (it is possible that further emits inrun()
have to be implemented later).because of the frequent call of the method by the qtimer and the need to start threads again and again, i decided to use the threadpool.
unfortunately while running the application in debug mode the program crashes on start with the error message:
ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread ...
the strange thing is that when starting the program in release mode it runs without any problems and errors.
i tried to change the setAutoDelete() value of the qrunnable in QWidgetClassInMainThread::startImageProcessingIteration(), with more or less success (see comment in code).
now i am a bit clueless and hope any qt pros can give me some tips or suggestions ;) thank you guys!
Code (shortened):
QWidgetClassInMainThread.hpp
class QWidgetClassInMainThread : public QWidget { Q_OBJECT private: QLabel label{this}; QPixmap pixmap{}; QTimer imageTimer{this}; QThreadPool threadpool{this}; void startImageProcessingIteration(); void setImageInQLabel(QImage); public: QWidgetClassInMainThread(QWidget *parent = nullptr); };
QWidgetClassInMainThread.cpp
Q_DECLARE_METATYPE(QImage); QWidgetClassInMainThread::QWidgetClassInMainThread(QWidget *parent) : QWidget(parent) { qRegisterMetaType<QImage>(); // connect qtimer to startImageProcessingIteration() connect(&imageTimer, &QTimer::timeout, this, &QWidgetClassInMainThread::startImageProcessingIteration); } // method called every ~20ms to ~50ms by qtimer void QWidgetClassInMainThread::startImageProcessingIteration() { // create new qrunnable and pass cloned opencv mat image, from camera object, in constructor auto *iteration = new CalculationRunnable(this, camera.getCurrentOpenCVImage()); // connect emit from iteration object with setImageInQLabel() connect(iteration, &CalculationRunnable::sendImageToMainThread, this, &QWidgetClassInMainThread::setImageInQLabel); // VARIANT 1 iteration->setAutoDelete(true); // error with msvc in debug mode, AND ONLY in debug mode! -> assert failure ... cannot send event to object owned by other thread ... // VARIANT 2 iteration->setAutoDelete(false); // no errors but what todo with the iteration pointer variable because it will not be deleted by the threadpool? // put qrunnable in threadpool threadpool.start(iteration); } void QWidgetClassInMainThread::setImageInQLabel(QImage qImage) { // update qpixmap and qlabel in main thread pixmap.convertFromImage(qImage); label.setPixmap(_pix); }
CalculationRunnable.hpp
class CalculationRunnable : public QObject, public QRunnable { Q_OBJECT private: cv::Mat openCVImage; public: CalculationRunnable(QObject *parent, cv::Mat openCVImage); public: void run() override; signals: void sendImageToMainThread(QImage); };
CalculationRunnable.cpp
CalculationRunnable::CalculationRunnable(QObject *parent, cv::Mat openCVImage) : QObject (parent) , QRunnable () , openCVImage(openCVImage) {} void CalculationRunnable::run() { auto image = openCVImage; /* * Do something with cv image. * do other emits. */ // convert OpenCV image to QImage QImage qimage = cvMatToQImage(image); // send signal with qimage to main thread to update qpixmap/qlabel emit sendImageToMainThread(qimage); }
-
Hi and welcome to devnet,
Since you make it deletable, don't pass a parent to it.
One other thing, for all QObject based variables that you keep on stack, don't set the parent. This will trigger a double delete. Once because to the parent/child relationship and once because of the destruction happening when going out of scope.
-
thanks for your reply and sorry for long time to answer.
so you suggest to create the qrunnable that way:
// no this in ctor auto *iteration = new CalculationRunnable(nullptr, camera.getCurrentOpenCVImage()); ... iteration->setAutoDelete(true); threadpool.start(iteration);
and about your second recommendation, do you mean i should not pass
this
in the class variables:class QWidgetClassInMainThread : public QWidget { ... QTimer imageTimer{}; // no this QThreadPool threadpool{}; // no this ...
-
Class members are "local" variable so they will get destroy on instance destruction. If you give a parent to these objects, then they will also be part of the child tree of the widget and deletion will also be triggered on them.