[SOLVED]Multithread question



  • Hi guys, I hope you can help me fix this:

    I'd wrote a "Sql Manager" which works in an other thread in order to not freeze my app during queries.

    I use this to retrieve search results, and I'm calling QEventLoop::exec() in order to wait until query is completed. I have this piece of code:

    @
    void app::search(QString text)
    {
    timer->start(1000);
    sqlmanager->query(/query statements/ text);
    while(timer->isActive() && canFetchMore())
    {
    fetchMore();
    }
    }
    @

    and

    @
    void app:fetchMore()
    {
    sqlmanager->query(/query statements/);
    //set canFetchMore
    }
    @

    My problem is:

    Both search and fetchMore functions use the same Sql Manager. When I call search before canFetch is completed it starts and finishes this function before fetchMore is completed, proceeding this way:

    Start search;
    Finish search;
    Start canFetch;
    Start search; (new call)
    Finish search;
    Finish canFetch;

    Due to the signal/slot mechanism used to communicate with Sql Manager (in other thread), I'm getting the response from canFetch into new search call.

    Does anyone know how to solve this?

    Thanks.



  • A few questions:

    • which thread owns your "app" instance ?
    • how do you created your thread ?
    • how are you using your qeventloop ?
    • what's in your canFetch method ? (and is it canFetchMore ?)

    We're lacking a few details to help you.



  • The problem may be sourced from different reasons. If you can give more information about the sql-manager thread, it could be given more useful information. As far as I saw from your scripts, you should omit timer->isActive() and use QTime *t and t->elapsed() for your purpose. Also calling the thread is a right manner is important, if you cause a way to run it abnormally, you can get irregular results.

    Please look at here for problems related with timer :

    "here":http://stackoverflow.com/questions/4695044/qt-how-to-use-qtimer-to-print-a-message-to-a-qtextbrowser-every-10-seconds
    and
    "here":http://stackoverflow.com/questions/4834074/qt-how-to-delay-a-program-while-qtimer-is-active



  • ranger0 - do you use queued connection for signals and slots?



  • [quote author="Adrien Leravat" date="1359637461"]A few questions:

    • which thread owns your "app" instance ?
    • how do you created your thread ?
    • how are you using your qeventloop ?
    • what's in your canFetch method ? (and is it canFetchMore ?)

    We're lacking a few details to help you.[/quote]

    Adrien,

    My "app" instance is owned by GUI thread.
    My thread was created as follows:
    @
    dbObject * db = new dbObject;
    QThread * threaddb = new QThread(this);

    //connections

    db->moveToThread(threaddb);
    threaddb->start(QThread::IdlePriority);
    @

    QEventLoop is used in order to wait thread response as follows:
    @
    //Emit signal to thread
    QEventLoop * loop = new QEventLoop;
    loop->exec();
    //Return thread response
    @

    My canFetchMore method just returns whether is possible fetch more results, which is different from fetchMore method which actually calls Sql Mannager.

    Thanks for your help.



  • [quote author="utcenter" date="1359650713"]ranger0 - do you use queued connection for signals and slots?[/quote]

    utcenter,

    As Qt 4 documentation says, it's default when connecting different threads.

    It's working right.

    Thank you.



  • From what I saw, I'd say there is a little design problem.

    If you want to use a thread to fetchData for 1 second (that's what I understand from you code, not each second), then you could:

    • Add a newSearchStarted to app
    • Add a search slot to dbObject
    • Add a resultsFound signal to dbObject
    • Add a onResultsFound slot to app

    This way you communicate asynchronously with your dbObject, only with signals/slots, and you dbObject will let you know when it found new results.

    @
    dbObject * db = new dbObject;
    QThread * threaddb = new QThread(this);

    //connections
    db->moveToThread(threaddb);
    connect(db, SIGNAL(resultsFound(...), this, SLOT(onResultsFound(...));
    connect(this, SIGNAL(newSearchStarted(const QString&)), db, SLOT(search(const QString&));
    threaddb->start(QThread::IdlePriority);
    @

    In your dbObject class
    @
    dbObject::dbObject()
    {
    connect(timer, SIGNAL(timeout()), this, SLOT(fetchMore()));
    timer->setSingleShot(true);
    timer->setDuration(0);
    }
    void dbObject::newSearchStarted(const QString& text)
    {
    // Do what's needed to stop previous running search if any
    // And init search/save search text

    // Start the "fetchMore" loop
    fetchMore();
    }

    void dbObject::fetchMore()
    {
    sqlmanager->query(/query statements/ text);
    emit resultsFound(...); // Parse you results

    if (canFetchMore())
    {
    // Post a signal that will be processed immediately as thread returns to its event loop, but yet can be interrupted ( for exemple by a new search)
    timer->start();
    }
    }
    @

    In you app class code
    @
    void app::onSearchButtonClicked()
    {
    ...
    emit newSearchStarted(sSearchString);
    ...
    }

    void app::onResultsFound(...)
    {
    // Handle your data
    }
    @

    Finally, I don't think you need a QEventLoop. Simply a thread owning your dbObject, and signal/slots



  • Hi Adrien,

    I appreciate very much your help!

    I'd like to discuss with you how you would stop running searches. Due to signal/slot mechanism, all signals from different threads are queued. It means that if I ask for a new search, it will be processed only when the current one is finished and therefore, there will be nothing to stop.

    Due to details not shown here yet, I really need a QEventLoop to wait until dbObject response. So I could solve this problem using the code below:

    @
    void app::search(QString text)
    {
    if(!searchIsRunning)
    {
    searchIsRunning = true;
    timer->start(1000);
    sqlmanager->query(/query statements/ text);
    while(timer->isActive() && canFetchMore())
    {
    fetchMore();
    }
    searchIsRunning = false;
    emit searchFinished();
    }
    else
    {
    searchText.append(text); //QStringList
    connect(this,SIGNAL(searchFinished()),this,SLOT(newSearch()));
    }
    }

    void app:newSearch()
    {
    disconnect(this,SIGNAL(searchFinished()),0,0);
    search(searchText.last());
    searchText.clear();
    }
    @

    This way I prevent two simultaneous calls to dbObject, but I think that killing the previous call and start a new one would be a better solution. What do you think?

    Best regards and thank you for your help.



  • [quote author="ranger0" date="1359713906"]Due to signal/slot mechanism, all signals from different threads are queued. It means that if I ask for a new search, it will be processed only when the current one is finished and therefore, there will be nothing to stop.[/quote]

    Well... actually no, because of the use of a QTimer, that will post an event that will be processed the same way as the newSearchStarted signal in my example.

    • Thread1- newSearchStarted signal ->>
    • Thread2->> search slot from Thread1 -> fetchMore (starts QTimer)
    • Thread1- newSearchStarted signal ->>
    • Thread2->> fetchMore from QTimer (starts QTimer)
    • Thread2->> search slot from Thread1 -> stop previous search (for exemple by incrementing a "searchId" variable, tested at the beginning of fetchMore) and call fetchMore (starts QTimer)

    So if you fetching mecanism allow to execute multiple consecutive queries done in fetchMore, the search will stop half way if another search is requested. Infinite loops like the one you did with while(timer->isActive()) should be avoided as it prevents the thread from processing events until done. Which is not the case using a timer with a 0ms timeout. This is a common way to achieve this, as pointed by eyyakeen.


Log in to reply
 

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