简体   繁体   中英

QThread interthread communication: strange behavior connecting to &QThread::quit vs connecting to a lambda [&thread] { thread->quit();}

I have the following setup:

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // Create the DBManager that will live for the entire
    // duration of the application
    auto dbManagerThread = std::make_unique<QThread>();
    auto dbManager = std::make_unique<DBManager>();
    dbManager->moveToThread(dbManagerThread.get());
    dbManagerThread->start();


    // for the initialization of the application, create the
    // InitHelper object that will utilize the DBManager
    auto initHelper = new InitHelper();
    auto initHelperThread = new QThread();
    initHelper -> moveToThread(initHelperThread);
    // wire InitHelper and its thread
    QObject::connect(initHelperThread, &QThread::started, initHelper, &InitHelper::run);
    QObject::connect(initHelper, &InitHelper::finished, [&initHelperThread] { initHelperThread->quit();});
    QObject::connect(initHelper, &InitHelper::finished, initHelper, &InitHelper::deleteLater);
    QObject::connect(initHelperThread, &QThread::finished, initHelperThread, &QThread::deleteLater);

    // wire InitHelper and DBManager
    QObject::connect(initHelper, &InitHelper::queryDB, dbManager.get(), &DBManager::processQuery);
    QObject::connect(dbManager.get(), &DBManager::queryResult, initHelper, &InitHelper::processQueryResult);


    // block until all information is gathered
    initHelperThread->start();
    initHelperThread->wait();
    std::cout << "Initialization completed." << std::endl;

    // cleanup
    dbManagerThread->quit();
    QObject::connect(dbManagerThread.get(), &QThread::finished, &a, &QCoreApplication::quit);
    return a.exec();
}

The idea is that I have the DBManager which is doing asynchronous database accesses and hence it utilized throughout the entire application. During the initialization of the application I need to retrieve some information from the database and I'd like to use the already existing DBManager for that.

However, since I need the information from the database to create all other objects, I want to block the futher execution of the main thread until my InitHelper has retrieved all required information from the DBManager.

Since the code above does exactly what it is supposed to, I don't think it matters how exactly InitHelper and DBManager are implemented and I have omitted them here.

What I find confusing is the fact that I need to use the lambda in the second line in the 'wire InitHelper and its thread' part. If I replace

QObject::connect(initHelper, &InitHelper::finished, [&initHelperThread] { initHelperThread->quit();});

by

QObject::connect(initHelper, &InitHelper::finished, initHelperThread, &QThread::quit);

apparently the thread never shuts down and so "Initialization completed" will never be print to stdout.

This is the output when using the connection to the lambda:

InitHelper: Initialization started...
DBManager: processing query...
InitHelper: processing query result
Initialization completed.

whereas when connecting to the slot directly, I get this output:

InitHelper: Initialization started...
DBManager: processing query...
InitHelper: processing query result

Does someone know why there is a difference when connecting to the lambda instead of connecting to the slot directly? And how can I write the connect without a lambda?

In your code:

QObject::connect(initHelper, &InitHelper::finished, [&initHelperThread] { initHelperThread->quit();});

Is similar to:

QObject::connect(initHelper, &InitHelper::finished, initHelperThread, &QThread::quit, Qt::DirectConnection);

It basically means that you are directly calling the function instead of using the event loop.

The reason, I believe, that your "correct" code does not work (ie when you use the Qt::QueuedConnection - which is default) is because you have not started the Qt event loop running by calling a.exec();

To solve this, I would remove your wait() function and move your "startup" code into a startup class (a bit like your initHelper). Connect a signal from the finished of your initHelper to the start() function (or whatever you want to call it) of your startup class.

edit

you might be able to do this (just read something similar):

int ret = a.exec();
initHelperThread->wait();
std::cout << "Initialization completed." << std::endl;
    :
  do other stuff
    :
return ret;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM