[SOLVED] Accessing objects living in another (running) thread in a thread-safe way
-
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. -
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@
-
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.
-
[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.
-
[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!