[SOLVED] Accessing objects living in another (running) thread in a thread-safe way
-
wrote on 3 Jan 2015, 01:34 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.
-
wrote on 3 Jan 2015, 05:45 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
-
wrote on 3 Jan 2015, 10:03 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.
-
wrote on 3 Jan 2015, 14:43 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).
-
wrote on 3 Jan 2015, 17:39 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."
-
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. -
wrote on 3 Jan 2015, 20:08 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_OBJECTpublic:
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_OBJECTpublic:
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@
-
wrote on 3 Jan 2015, 20:25 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.
-
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.
-
wrote on 4 Jan 2015, 01:23 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).
-
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?
-
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.
-
wrote on 4 Jan 2015, 13:07 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 ;)
-
You're most welcome. :) Happy coding!
1/14