0

I have a console application where after a timeout signal, a 2D matrix (15*1200) should be parsed element-by element and inserted to a database. Since the operation is time-consuming, I perform the insertion in a new thread using QConcurrent::run.

However, due to timeout signals, several threads may start before one finished, so multiple accesses to the database may occur. As a solution, I was trying to buffer all database operations in a specific thread, in other words, assign a specific thread to the database class, but do not know how to do so.

Saeed
  • 742
  • 1
  • 7
  • 21
  • You could use a blocking queue in your thread, so that database operations can be posted in the queue, and processed sequentially by your thread. See this, for example of an implmentation of a blocking queue: http://stackoverflow.com/questions/12805041/c-equivalent-to-javas-blockingqueue – shrike Sep 03 '16 at 08:06
  • You may use a worker object with slots for all database operations. Read Qt documentation for details: http://doc.qt.io/qt-5/qthread.html#details – Dmitry Sazonov Sep 03 '16 at 08:35

1 Answers1

2

Your problem is a classical concurrent data analysis problem. Have you tried using std::mutex? Here's how you use it:

You create some variable std::mutex (mutex = mutual exclusion) that's accessible by all the relevant threads.

std::mutex myLock;

and then, let's say that the function that processes the data looks like this:

void processData(const Data& myData)
{
    ProcessedData d = parseData();
    insertToDatabase(d);
}

Now from what I understand, you're afraid that multiple threads will call insertToDatabase(d) simultaneously. Now to solve this issue, simply do the following:

void processData(const Data& myData)
{
    ProcessedData d = parseData();
    myLock.lock();
    insertToDatabase(d);
    myLock.unlock();
}

Now with this, if another thread tries to access the same function, it will block until another all other threads are finished. So threads are mutually excluded from accessing the call together.

More about this:

Caveats:

  1. This mutex object must be the same one that all the threads see, otherwise this is useless. So either make it global (bad idea, but will work), or put it in a the class that will do the calls.

  2. Mutex objects are non-copyable. So if you include them in a class, you should either make the mutex object a pointer, or you should reimplement the copy constructor of that class to prevent copying that mutex, or make your class noncopyable using delete:

    class MyClass
    {
        //... stuff
        MyClass(const MyClass& src) = delete;
        //... other stuff
    };
    
  3. There are way more fancier ways to use std::mutex, including std::lock_guard and std::unique_lock, which take ownership of the mutex and do the lock for you. This are good to use if you know that the call insertToDatabase(d); could throw an exception. In that case, using only the code I wrote will not unlock the mutex, and the program will reach a deadlock.

In the example I provided, here's how you use lock_guard:

void processData(const Data& myData)
{
    ProcessedData d = parseData();
    std::lock_guard<std::mutex> guard(myLock);
    insertToDatabase(d);
    //it will unlock automatically at the end of this function, when the object "guard" is destroyed
}
  1. Be aware that calling lock() twice by the same thread has undefined behavior.

  2. Everything I did above is C++11.

  3. If you're going to deal with multiple threads, I recommend that you start reading about data management with multiple threads. This is a good book.

  4. If you insist on using Qt stuff, here's the same thing from Qt... QMutex.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189
  • Your answer is great, but the [Qt documentation](https://doc.qt.io/qt-5/threads-modules.html#threads-and-the-sql-module) states that *A connection can only be used from within the thread that created it*. Not sure if using locks around your database calls can save you from trouble. But I would say that using locks around **your calls** can't stop Qt itself from changing some structures in `QSqlDatabase` if for example a [notification](https://doc.qt.io/qt-5/qsqldriver.html#notification-1) occurs while you are in the middle of your `insertToDatabase` functon. – Mike Sep 03 '16 at 23:07
  • @Mike While I see what you mean but not fully understand it, I can't suggest a better answer based on the question. There's very limited information. So I suggest the question has to be updated to contain more details, or the program at hand has to be modified to be thread-safe. Sometimes thread-safety requires changing the whole philosophy and plan of a program. I wish that the asker reads your comment, to be aware of how complicated thread-safety is, and make a decision. – The Quantum Physicist Sep 03 '16 at 23:24