Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Update: Forum Guidelines & Code of Conduct

    [SOLVED] Accessing objects living in another (running) thread in a thread-safe way

    General and Desktop
    5
    14
    6523
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • S
      Sigsegv last edited by

      I have 2 threads: a GUI thread and a worker thread.

      The worker thread will be writing values to a list for several seconds. Meanwhile, I would need to read and remove values from the list from the GUI thread while the working thread is still running.

      I suppose I could use a direct connection signal for the remove operation (although I'm not sure if that would be considered as a good solution) but the real problem is the reading operation.

      I would need to read values from the list (living in the still-busy worker thread) and process them right away within the GUI thread, so I can't use queued connection signals since they would be blocked until the completion of the working thread. I can't use direct connection signals either, since the reading operations are executed within a larger method that can't be interrupted to process incoming signals. Finally, I can't find any suitable way to use a mutex since the writing and reading operations are executed within different methods.

      Any ideas will be appreciated. Thanks in advance.

      1 Reply Last reply Reply Quote 0
      • D
        dvb0222 last edited by

        Here is a slightly different pattern which might suit what you want to achieve.

        I am guessing that you want to have the worker thread read or generate some objects, and then add them to a list which will be read and displayed by an object on the GUI thread

        Instead of creating a list object for this, how about just having the method on the worker thread post events to the receiver object (on the GUI thread). This way the event queue of the GUI thread serves the purpose of your list. You can post an event to an object on another thread using http://doc.qt.io/qt-5/qcoreapplication.html#postEvent. Note that this static method is thread safe.

        Now all you need to do is uniquely identify your events with a new event type, subclass QEvent to contain the data you want to send, and implement http://doc.qt.io/qt-5/qobject.html#event on the object in the GUI thread to consume the events.

        Read about the event system here: http://doc.qt.io/qt-5/eventsandfilters.html


        David Van Beveren
        Oak Park Technology Corp.
        Malibu, California
        vbdavid@gmail.com

        1 Reply Last reply Reply Quote 1
        • C
          ckakman last edited by

          Hi,

          I think your problem is to modify a list by the thread simultaneously without waiting one of the modifying agents to finish its job.

          I can think of two solutions that may interest you besides the one proposed by dvb0222:

          Don't use a worker thread: Whatever triggers the worker thread to start adding new items to the list for several seconds, can trigger the process in the GUI thread the as well. Since you don't want to block the GUI thread so that the application becomes unresponsive, you may use QCoreApplication::processEvents() creatively so that the items are added to the list one by one while you also read and remove items one by one.

          Don't modify the list in the worker thread: Send the value objects to GUI thread and process it there. If the objects are expensive to copy, use a shared pointer or make the value object explicitly shared if you care for aesthetics. Here you may also need some creative QCoreApplication::processEvents() usage since you want to interleave add and read/remove operations.

          1 Reply Last reply Reply Quote 0
          • S
            Sigsegv last edited by

            Thank you for your replies, but I fail to see how can I use event processing to receive reading values right away. In my app the user can trigger key events which need to read values from the list (and process them). However, should the readings be returned using events (or signals, for that matter), I don't see how can I receive demanded values without exiting the current method (in which the readings have been demanded).

            1 Reply Last reply Reply Quote 0
            • C
              ckakman last edited by

              Hi,

              It is really difficult to understand one's problem when there is no code but general and rather vague description of the issue. Your last response doesn't ring a bell for me because I don't know your application. You haven't shared enough for us to understand your assumptions and constraints.

              • Why can't you read the list when the user triggers a key event?
              • What does it mean "to return a value" when you can just "show" a value on the UI when the user clicks a button?
              • Why processing the list and populating the list must be on different threads?
              • Why can't you send the items to be added to the list from the worker thread to the UI thread and add there so that you don't need to synchronize the access to the list?
              • Have you ever used QCoreApplication::processEvents() in a loop so that the UI remains responsive?

              I can go on and on with much more questions because this description is not enough:

              "The worker thread will be writing values to a list for several seconds. Meanwhile, I would need to read and remove values from the list from the GUI thread while the working thread is still running."

              1 Reply Last reply Reply Quote 0
              • Chris Kawa
                Chris Kawa Moderators last edited by

                I might be over-simplifying but from the description it sounds all you need is a thread-safe list.
                Should be pretty easy to either make one yourself or just create an access wrapper for the QList i.e. a class that would hold a pointer to the list and have mutex guarded access methods like append(), remove(), at() etc.

                1 Reply Last reply Reply Quote 0
                • S
                  Sigsegv last edited by

                  Here is a minimal simplification of what I want to accomplish:

                  In this example, I use public calls for removing and reading values. Obviously, these calls are not thread-safe, but I need them in order to read the values from the list inmediately when the user demands them. The goal would be finding a thread-safe way to do the same.

                  main.cpp:
                  @#include "mainwindow.h"
                  #include <QApplication>

                  int main(int argc, char *argv[])
                  {
                  QApplication a(argc, argv);
                  MainWindow w;
                  w.show();
                  return a.exec();
                  }@

                  mainwindow.h:
                  @#ifndef MAINWINDOW_H
                  #define MAINWINDOW_H

                  #include "ui_mainwindow.h"
                  #include <QMainWindow>
                  #include <QObject>
                  #include <QThread>
                  #include <QKeyEvent>

                  class Worker : public QObject
                  {
                  Q_OBJECT

                  public:
                  QString read(int index)
                  {
                  return (index >= 0 && index < stringList.size()) ? stringList.at(index) : QString();
                  }
                  void remove(int index)
                  {
                  stringList.removeAt(index);
                  }

                  public slots:
                  void Load()
                  {
                  for(unsigned int i=0; i<999999999; i++)
                  stringList << QString::number(i);
                  }

                  private:
                  QStringList stringList;
                  };

                  namespace Ui { class MainWindow; }

                  class MainWindow : public QMainWindow
                  {
                  Q_OBJECT

                  public:
                  explicit MainWindow(QWidget *parent = 0) : QMainWindow(parent), ui(new Ui::MainWindow)
                  {
                  ui->setupUi(this);

                      worker = new Worker;
                      worker->moveToThread(&workerThread);
                      connect(&workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
                      connect(this, SIGNAL(Load()), worker, SLOT(Load()));
                      workerThread.start();
                  
                      emit Load();
                  }
                  ~MainWindow()
                  {
                      delete ui;
                      workerThread.quit();
                      workerThread.wait();
                      delete worker;
                  }
                  

                  signals:
                  void Load();

                  private:
                  Ui::MainWindow *ui;
                  Worker *worker;
                  QThread workerThread;

                  private slots:
                  void on_spinBox_valueChanged(int newValue)
                  {
                  ui->label->setText(worker->read(newValue));
                  }

                  protected:
                  void keyPressEvent(QKeyEvent *event)
                  {
                  if(event->key() == Qt::Key_Delete)
                  {
                  worker->remove(ui->spinBox->value());
                  ui->label->setText(QString());
                  }
                  }
                  };

                  #endif // MAINWINDOW_H@

                  1 Reply Last reply Reply Quote 0
                  • C
                    ckakman last edited by

                    Hi,

                    I can make a couple of suggestions:

                    • Keep the list in mainwindow. Send items from worker to mainwindow via a signal. It is thread-safe. UI thread not blocked. But lots of async calls.
                    • Keep the list in mainwindow. Store a certain number of items in a list or preferably vector. Send the vector to mainwindow via a signal. Resume storing items in an empty vector, etc. Fewer async calls.
                    • Don't create a worker thread. Start adding items to a list or vector, but call qApp->processEvents() after every N items. You should determine a reasonable N so that the UI remains responsive. No async calls, but blocks UI thread for small durations which may not be noticable by the user if N is chosen properly.
                    1 Reply Last reply Reply Quote 0
                    • Chris Kawa
                      Chris Kawa Moderators last edited by

                      So exactly what I suggested. Just wrap the list << operator in a method of Worker class and then guard that and the read() and remove() with a mutex member variable.

                      1 Reply Last reply Reply Quote 0
                      • S
                        Sigsegv last edited by

                        [quote author="Chris Kawa" date="1420317000"]So exactly what I suggested. Just wrap the list << operator in a method of Worker class and then guard that and the read() and remove() with a mutex member variable.[/quote]

                        I understand mutexes as mechanisms that ensures access serialization between threads within a particular section of code, so that section of code is executed by one thread at a time. Since, in my example, readings and writings are executed from different methods (both executed by different threads), I fail to see how the use mutexes can grant thread-safety to my list operations. Assuming you mean something like this:

                        @read()
                        {
                        mutex.lock();
                        //read from the list
                        mutex.unlock();
                        }@
                        @write()
                        {
                        mutex.lock();
                        //write on the list
                        mutex.unlock();
                        }@
                        @remove()
                        {
                        mutex.lock();
                        //remove an item from the list
                        mutex.unlock();
                        }@

                        Only one thread can execute read(), write() or remove() at the same time, but up to 3 threads may access the list simultaneously (if the threads call different methods).

                        1 Reply Last reply Reply Quote 0
                        • Chris Kawa
                          Chris Kawa Moderators last edited by

                          bq. Assuming you mean something like this

                          I would suggest QMutexLocker, but in principle yes, that's it.

                          bq. Only one thread can execute read(), write() or remove() at the same time, but up to 3 threads may access the list simultaneously

                          I don't understand. What do you mean by "access"? read(), write() and remove() are the access and the mutex ensures that only one at a time happens, so after each of these operations the list is in a coherent state. Do you want to "access" the list in some other way than through these methods?

                          1 Reply Last reply Reply Quote 0
                          • JKSH
                            JKSH Moderators last edited by

                            Hi,
                            [quote author="Sigsegv" date="1420334628"]I understand mutexes as mechanisms that ensures access serialization between threads within a particular section of code, so that section of code is executed by one thread at a time. Since, in my example, readings and writings are executed from different methods (both executed by different threads), I fail to see how the use mutexes can grant thread-safety to my list operations. Assuming you mean something like this:

                            @read()
                            {
                            mutex.lock();
                            //read from the list
                            mutex.unlock();
                            }@
                            @write()
                            {
                            mutex.lock();
                            //write on the list
                            mutex.unlock();
                            }@
                            @remove()
                            {
                            mutex.lock();
                            //remove an item from the list
                            mutex.unlock();
                            }@

                            Only one thread can execute read(), write() or remove() at the same time, but up to 3 threads may access the list simultaneously (if the threads call different methods).[/quote]You have misunderstood how mutexes work.

                            Suppose two different functions (read() and write() ) lock the same mutex object.

                            Scenario 1: Thread X calls read() first, then Thread Y calls write() before X has finished reading. Y will be blocked until X unlocks the mutex -- The write() function will wait for the read() function to finish before proceeding.

                            Scenario 2: Thread Y calls write() first, then Thread X calls read() before Y has finished writing. X will be blocked until Y unlocks the mutex -- The read() function will wait for the write() function to finish before proceeding.

                            You might find the "Synchronizing Threads":http://doc.qt.io/qt-5/threads-synchronizing.html article helpful.

                            Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                            1 Reply Last reply Reply Quote 0
                            • S
                              Sigsegv last edited by

                              [quote author="JKSH" date="1420364578"]Hi,
                              You have misunderstood how mutexes work.

                              Suppose two different functions (read() and write() ) lock the same mutex object.

                              Scenario 1: Thread X calls read() first, then Thread Y calls write() before X has finished reading. Y will be blocked until X unlocks the mutex -- The write() function will wait for the read() function to finish before proceeding.

                              Scenario 2: Thread Y calls write() first, then Thread X calls read() before Y has finished writing. X will be blocked until Y unlocks the mutex -- The read() function will wait for the write() function to finish before proceeding.

                              You might find the "Synchronizing Threads":http://doc.qt.io/qt-5/threads-synchronizing.html article helpful.[/quote]

                              You're right, I misunderstood how mutexes work.
                              I'll consider the use of public functions wrapped in mutexes as the optimal solution for my problem and mark this thread as solved.

                              Thank you all for your replies and patience ;)

                              1 Reply Last reply Reply Quote 0
                              • JKSH
                                JKSH Moderators last edited by

                                You're most welcome. :) Happy coding!

                                Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                                1 Reply Last reply Reply Quote 0
                                • First post
                                  Last post