Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

slots are being called multiple times



  • I have the following code that gets called whenever a user opens a file, so this code can be called multiple times.

    {   
        QWidget *tab = getTabWidgetByName(name);
    
        mIO = new MIO(this);
        mIO->moveToThread(&mIOThread);
    
        connect(&mIOThread, &QThread::finished, mIO, &QObject::deleteLater);
        connect(this, &MainWindow::startMIO, this, 
        [this] () -> void { mIO->operator()(); }, Qt::UniqueConnection);
        connect(mIO, &MIO::notifyMIOFinished, this,
        &MainWindow::mIOFinished, Qt::UniqueConnection);
    
        mIOThread.start();
    
        emit startMIO();
    }
    

    It works fine until the second time it's called. When run, it appears to be emitting startMIO() twice. The functor mIO->operator()() is certainly called twice somehow and I'm wondering if I'm starting the thread correctly as I call start() on mIOThread. It shouldn't do anything as the constructor is basically empty, and then when emit startMIO() is called, it starts the functor which is where all the work is at.

    I saw several other threads about this, and have added Qt::UniqueConnection, but that doesn't seem to help.

    Here's a bit about the MIO object.

    class MIO : public QObject
    {       
        Q_OBJECT
        
        private:
            MIO(MainWindow *win);
            ~MIO();
    
            void operator()();
        // ...
    }
    
    MIO::MIO(MainWindow *win) : mainWin(win) { }
    
    void MIO::operator()()
    {
        // lots of stuff
    }
    

    Thoughts?


  • Moderators

    It's a problem with your usage of Qt::UniqueConnection in a connect statement with a lambda. Each time a different instance of that lambda object is created so the connection to it is made. The way Qt::UniqueConnection works is it compares the addresses of the objects and function pointers of the signal and slot and doesn't make a connection if all of thouse match something existing.

    In short Qt::UniqueConnection doesn't work with lambdas so replace the lambda with a normal member function.
    Another solution is to store the result of the connect statement ( a QMetaObject::Connection object) and use it to call disconnect after the connection is triggered.

    Also note that your usage of connect is kinda dangerous, You passed this as the first and third parameter so the connection lives as long as this lives. Inside the lambda you are using that mIO object though, that can be destroyed anytime so your app may crash if mIO is deleted and the signal is emited after that. It's better to tie the connection to the object you're using in the lambda, so instead of using this as the third parameter you can use mIO. This way if either this or mIO is deleted the connection will be severed automatically.

    Another consideration is that slots are executed in the thread of the target object, so if you pass this as the third parameter the lambda will be called on the thread that this lives in, not mIO and that's probably not what you want. Passing mIO as the third argument would solve that too.



  • edit What @Chris-Kawa said.

    Given the limited context, use of a dedicated QThread seems overly complicated. QtConcurrent::run may be a better solution.


Log in to reply