Wait for result from other thread



  • Hi,

    I have spent countless hours reading about QThreads, but I feel that I still miss some vital knowledge on QThreads and eventloops. Below is an example of requesting data from another thread and then wait for the result. For some reason this doesn't work and I was hoping someone could explain why. The queueAction signal is triggered from a button in QML.

    //Main
    Requester requester;
    Provider provider;
    QThread* requestThread = new QThread();
    QThread* provideThread = new QThread();
    QThread::currentThread()->setObjectName("MainThread");
    requestThread->setObjectName("RequestThread");
    provideThread->setObjectName("ProvideThread");
    requester.moveToThread(requestThread);
    provider.moveToThread(provideThread);
    QObject::connect(&requester, SIGNAL(requestData()), &provider, SLOT(handleRequest()));
    QObject::connect(&provider, SIGNAL(sendResult(int)), &requester, SLOT(receiveResult(int)));
    requestThread->start();
    provideThread->start();
    
    //Requester.h
    class Requester : public QObject
    {
        Q_OBJECT
    public:
        Requester() :
            loop()
        {
            connect(this, SIGNAL(wakeLoop()), &loop, SLOT(quit()));
            connect(this, SIGNAL(queueAction()), this, SLOT(action()),Qt::QueuedConnection);
        }
    
    signals:
        void wakeLoop();
        void queueAction();
        void requestData();
    
    public slots:
        void action() {
            qDebug() << "Sending request in thread" << thread()->currentThread()->objectName();
            emit requestData();
            loop.exec();
            qDebug() << "Finished waiting for result in thread" << thread()->currentThread()->objectName();
        }
    
        void receiveResult(int result) {
            qDebug() << "Received result" << result << "in thread" << thread()->currentThread()->objectName();
            emit wakeLoop();
        }
    
    private:
        QEventLoop loop;
    };
    
    //Provider.h
    class Provider : public QObject
    {
        Q_OBJECT
    public:
        Provider() {}
    
    signals:
        void sendResult(int result);
    
    public slots:
        void handleRequest()
        {
            qDebug() << "Received request in thread" << thread()->currentThread()->objectName();
            emit sendResult(2);
        }
    };
    

    The code above gives the following output:
    [D] Requester::action:27 - Sending request in thread "RequestThread"
    [D] Provider::handleRequest:20 - Received request in thread "ProvideThread"

    However If I keep Requester in main thread instead of moving it to a separate thread it works.
    Output:
    [D] Requester::action:27 - Sending request in thread "MainThread"
    [D] Provider::handleRequest:20 - Received request in thread "ProvideThread"
    [D] Requester::receiveResult:34 - Received result 2 in thread "MainThread"
    [D] Requester::action:30 - Finished waiting for result in thread "MainThread"



  • Do you know QFuture and QFutureInterface? Using them would make work a lot easier for you.

    You can send a QFutureInterface-object with an asynchronous signal from one thread to a worker-object in another thread while passing the future provided by the QFutureInterface-object to the thread that should receive the result.

    Don't wonder if you've never heard of QFutureInterface. It's part of QtConcurrent but not well documented. It's API is really simple. Most things you need you can find out yourself by just taking a short look into the header or using autocompletion in QtCreator. For a basic example take a look at: http://developers-club.com/posts/273743/

    I think that's what you should prefer because then you don't have to deal with own QEventLoops within QObjects within other QThreads - something that seems to be error prone to me.



  • I have some knowledge about QFuture, but if I have understood correctly the returned value will not be available until "later". In this specific case I cannot continue execution in the requesting thread until I know the returned value so I assume that I have to use some kind of wait anyway even if I would use QFuture/QFutureInterface.

    Part of the reason for this question was to get a deeper understanding on how threading and QEventloop works so if possible I would prefer a solution involving QThread instead of QFuture/QtConcurrent.


  • Moderators


  • Qt Champions 2016

    @raspe88 said in Wait for result from other thread:

    http://developers-club.com/posts/273743/

    SlotClosure<NoDeletePolicy> rotator
    {
       [this] { RotateFuncs (); },
       this,
       SIGNAL (rotateFuncs ()),
       nullptr
    };
    

    Ah, the orderliness of C++11 syntax .. we actually create an object here with a functor as first argument ... beautiful! To quote one Bob Hood from the mailing list:

    As much as the standards committee is trying to turn C++ into a scripting language, it still has it's roots in C, and Here Be Monsters for the uninitiated.

    @rdeem said in Wait for result from other thread:

    I have to use some kind of wait anyway even if I would use QFuture/QFutureInterface.

    @jsulm has suggested the better way. The simplest, low-level thing you can do is to just use a semaphore (untested, but should work):

    class Requester : public QObject
    {
        Q_OBJECT
    
    signals:
        void requestData();
        void needsResult();
    
    public:
        void action()
        {
            qDebug() << "Sending request in thread" << thread()->currentThread()->objectName();
            emit requestData();
            emit needsResult();
    
            qDebug() << "Finished waiting for result in thread" << thread()->currentThread()->objectName();
        }
    
    public slots:
        void receiveResult(int result)
        {
            qDebug() << "Received result" << result << "in thread" << thread()->currentThread()->objectName();
        }
    

    and

    class Provider : public QObject
    {
        Q_OBJECT
    
    signals:
        void resultReady(int result);
    
    public slots:
        void handleRequest()
        {
            qDebug() << "Received request in thread" << thread()->currentThread()->objectName();
            emit sendResult(2);
            lock.release();
        }
    
        void waitForResult()
        {
            lock.acquire();
        }
    
    private:
        QSemaphore lock;
    };
    

    Then you use as usual:

    int main(int argc, char ** argv)
    {
        QCoreApplication app(argc, argv);
    
        Requester requester;
        Provider provider;
       
        QThread requestThread, provideThread;
        requestThread.setObjectName("RequestThread");
        provideThread.setObjectName("ProvideThread");
        QThread::currentThread()->setObjectName("MainThread");
    
        requester.moveToThread(&requestThread);
        provider.moveToThread(&provideThread);
    
        QObject::connect(&requester, &Requester::requestData, &provider, &Provider::handleRequest);
        QObject::connect(&provider, &Provider::resultReady, &requester, &Requester::receiveResult);
        QObject::connect(&requester, &Requester::needsResult, &provider, &Provider::waitForResult, Qt::DirectConnection); //< Notice the connection type
    
        // These two are only for the sake of the example, so it doesn't run forever.
        QObject::connect(&provider, &Provider::resultReady, &requestThread, &QThread::quit);
        QObject::connect(&provider, &Provider::resultReady, &provideThread, &QThread::quit);
    
        requestThread.start();
        provideThread.start();
    
        requester.action();
    
        requestThread.wait();
        provideThread.wait();
    
        return 0;
    }
    

  • Lifetime Qt Champion

    Hi,

    I was wondering if your current setup shouldn't be modeled using a state machine ?

    • Step 1: request data
    • Transition from Step 1 to Step 2 on dataArrived signal emitted by Provider object
    • Step 2: do processing
    • Transition back to Step 1


  • @jsulm said in Wait for result from other thread:

    @rdeem Why not use http://doc.qt.io/qt-5.7/qfuture.html#waitForFinished

    Thanks! It looks like I should take a closer look at QFuture.

    @kshegunov said in Wait for result from other thread:

    @jsulm has suggested the better way. The simplest, low-level thing you can do is to just use a semaphore (untested, but should work):

    You may be right. I need to evaluate QFuture and your semaphore solution, but now I at least have a way forward. Thanks!

    @SGaist said in Wait for result from other thread:

    Hi,

    I was wondering if your current setup shouldn't be modeled using a state machine ?

    • Step 1: request data
    • Transition from Step 1 to Step 2 on dataArrived signal emitted by Provider object
    • Step 2: do processing
    • Transition back to Step 1

    Yes, this would require some refactoring, but it would be a very nice solution! I guess I need to rethink my design approach a little bit. :)


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.