Results 1 to 5 of 5

Thread: Please critique: solution to database connection and multiple threads

  1. #1
    Join Date
    May 2009
    Location
    Canada
    Posts
    163
    Thanks
    7
    Thanked 20 Times in 20 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Windows Android

    Default Please critique: solution to database connection and multiple threads

    Hello,

    As many of you are probably aware, Qt docs are quite clear about instances of QSqlDatabase and threads:

    A connection can only be used from within the thread that created it. Moving connections between threads or creating queries from a different thread is not supported.
    After migrating from raw MySQL libraries to a QSql-based approach, my application started behaving oddly. Evidently the raw MySQL does not have the cited constraint: my application indeed uses exactly two threads: the main thread, and a thread that receives sensor data. Under the current implementation, both threads do work with the database.

    The following illustrates the current solution to the problem, which takes a lazy-loading approach. Everything appears to work, but I would welcome any and all constructive criticism.

    Qt Code:
    1. // Controller.h
    2.  
    3. private:
    4. CustomDatabase * databaseConnectionForThread(const Qt::HANDLE threadID = QThread::currentThreadId());
    5.  
    6. QHash<Qt::HANDLE, CustomDatabase *> dbConnectionsByThread;
    7. QMutex threadMutex, sqlMutex;
    To copy to clipboard, switch view to plain text mode 

    Qt Code:
    1. // Controller.cpp
    2.  
    3. CustomDatabase * Controller::databaseConnectionForThread(const Qt::HANDLE threadID) {
    4. QMutexLocker locker(&threadMutex);
    5. static int numberForConnectionName = 0;
    6. CustomDatabase *dbConnection;
    7. if (dbConnectionsByThread.contains(threadID))
    8. dbConnection = dbConnectionsByThread.value(threadID);
    9. else {
    10. dbConnection = CustomDatabase::create(QString::number(++numberForConnectionName), this); // create() initializes and opens the connection it returns
    11. if (!dbConnection->isOpen())
    12. exitWithError(QString("Controller::databaseConnectionForThread() - Failed: %1!").arg(dbConnection->lastError()));
    13. dbConnectionsByThread.insert(threadID, dbConnection);
    14. }
    15. return dbConnection;
    16. }
    17.  
    18. void Controller::doStuffWithDatabase() {
    19. QMutexLocker locker(&sqlMutex);
    20. CustomDatabase *dbConnection = databaseConnectionForThread();
    21. // do stuff
    22. }
    To copy to clipboard, switch view to plain text mode 

    Thank you very much for your time.

  2. #2
    Join Date
    Jan 2006
    Location
    Graz, Austria
    Posts
    8,420
    Thanks
    37
    Thanked 1,545 Times in 1,495 Posts
    Qt products
    Qt3 Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Please critique: solution to database connection and multiple threads

    That looks ok, maybe use the pointer of QThread::currentThread() instead though.
    The docs for Qt::HANDLE say it can't be used for numeric comparison, which I think QHash does use.

    I am not sure you need the sqlMutex, you have per thread connections now anyway.

    An alternative approach would be to use different connectionNames for QSqlDatabase::addDatabase(), e.g. the pointer of the thread printed into a string.

    Cheers,
    _

  3. The following user says thank you to anda_skoa for this useful post:

    Urthas (19th October 2015)

  4. #3
    Join Date
    May 2009
    Location
    Canada
    Posts
    163
    Thanks
    7
    Thanked 20 Times in 20 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Windows Android

    Default Re: Please critique: solution to database connection and multiple threads

    Quote Originally Posted by anda_skoa View Post
    That looks ok, maybe use the pointer of QThread::currentThread() instead though.
    The docs for Qt::HANDLE say it can't be used for numeric comparison, which I think QHash does use.
    Ah, good catch, thank you!

    Generally speaking, is this a more or less standard way to address the original problem? I mean, it works fine but for the sake of argument, how else might one get around it? Are there certain situations in which certain approaches are best?

  5. #4
    Join Date
    May 2009
    Location
    Canada
    Posts
    163
    Thanks
    7
    Thanked 20 Times in 20 Posts
    Qt products
    Qt4 Qt5
    Platforms
    MacOS X Windows Android

    Default Re: Please critique: solution to database connection and multiple threads

    how else might one get around it? Are there certain situations in which certain approaches are best?
    I'm going to partially answer my own questions, for posterity.

    It just so happens that code executed in the sensor thread (which is not created by the application) uses an instance of a custom class that has-a QTimer. This resulted in the following warning:

    QObject::startTimer: Timers can only be used with threads started with QThread
    To get around this, I wrap the sensor input and pass it to the main thread instead of the lazy-load-and-cache database connections per thread approach:

    Qt Code:
    1. void Controller::onNotification(const Notification *notification, void *caller) {
    2. Controller *ref = static_cast<Controller *>(caller);
    3. NotificationWrapper wrapper(notification);
    4. QMetaObject::invokeMethod(ref, "doStuff", Qt::AutoConnection, Q_ARG(NotificationWrapper, wrapper));
    5. }
    To copy to clipboard, switch view to plain text mode 

    Note that there is some legwork involved in calling QMetaObject::invokeMethod() with a custom type. See http://doc.qt.io/qt-5/qmetaobject.html#invokeMethod.

    Since all the work is now on the main thread, only a single database connection is required. In addition, calls to QMutexLocker that were peppered throughout the code have been removed.
    Last edited by Urthas; 21st October 2015 at 22:47.

  6. #5
    Join Date
    Oct 2009
    Posts
    483
    Thanked 96 Times in 93 Posts
    Qt products
    Qt4 Qt5
    Platforms
    Unix/X11 Windows

    Default Re: Please critique: solution to database connection and multiple threads

    This is coming a bit late, especially since you have moved away from your one-connection-per-thread solution, but if you ever have to do something like that again, consider using thread-local storage (see QThreadStorage for Qt's implementation) instead of rolling out your own map protected by a mutex. Thread-local storage is supported by the C11 and C++11 standards, and is usually natively supported by the OS. IIRC a typical implementation consists in reserving a CPU register that points to a different memory area for each thread.

  7. The following user says thank you to yeye_olive for this useful post:

    Urthas (22nd October 2015)

Similar Threads

  1. Threads and database connection
    By probine in forum Qt Programming
    Replies: 9
    Last Post: 7th August 2013, 09:30
  2. SignalSlot Connection across the threads
    By shruti in forum Newbie
    Replies: 9
    Last Post: 29th March 2013, 11:30
  3. Replies: 5
    Last Post: 20th January 2013, 19:06
  4. Replies: 0
    Last Post: 21st April 2010, 20:13
  5. SQLite connection shared by several threads
    By jorgegarciar in forum Qt Programming
    Replies: 1
    Last Post: 23rd August 2009, 21:15

Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.