I know that this is next question about connect signal/slot mechanism between threads. I wrote working Worker application.
Main problem
I have worker class that has been moved to another thread. Second part of application is GUI interface with button. When I click button thread starts:
void MainWindow::startStopThreadA()
{
...
else
{
threadA = new QThread;
workerA = new WorkerObject;
workerA->setMessage("Thread A running");
workerA->moveToThread(threadA);
connect(threadA, SIGNAL(started()), workerA, SLOT(process()), Qt::QueuedConnection);
connect(workerA, SIGNAL(finished()), threadA, SLOT(quit()));
connect(workerA, SIGNAL(finished()), workerA, SLOT(deleteLater()));
connect(threadA, SIGNAL(finished()), threadA, SLOT(deleteLater()));
//Connect signal from thread with slot from MainWindow
connect(workerA, SIGNAL(printMessage(QString)), this, SLOT(printMessage(QString)), Qt::QueuedConnection);
threadA->start();
ui->threadAButton->setText("Stop A");
}
}
When thread starts then emits signal:
void WorkerObject::process(void)
{
//Infinity thread loop
forever
{
//Exit loop part
mutex.lock();
if(m_stop)
{
m_stop = false;
mutex.unlock();
break;
}
mutex.unlock();
//Hold/unhold loop part
mutex.lock();
if(!m_hold)
{
mutex.unlock();
//Here signal is emited
emit printMessage(messageStr); //That not works
//qDebug() << "Thread A test message."; //That works properly
}
mutex.unlock();
}
emit finished();
}
In main GUI thread I have timer for show that GUI thread works. So qDebug() works fine and prints messages from my thread. Also timer from GUI thread works fine and prints message inside textEdit GUI field.
Now when printMessage signal is emited, GUI thread executes slot method:
void MainWindow::printMessage(QString str)
{
ui->textEdit->append(str);
}
And this is most important part of my problem:
When signal printMessage from workerA object is connected with GUI slot printMessage with Qt::QueuedConnection my application hangs up. There is no possible to click something button or even exit app.
When signal/slot are connected with Qt::BlockingQueuedConnection everything works fine. Messages are emitted and received between threads and also GUI timer works fine.
So my question is why connection Qt::QueuedConnection causes that app freezes ?
I figured out this problem with #m7913d help.
You can try to perform a QThread::sleep inside your forever loop (f.e.
1 second) to check if it solves your problem.
So worker's thread emitted signal too frequently. Adding QThread::msleep(5) after emitted signal really helped. There ` also need to be included.
Related
In the following code omitting the waitForFinished() makes the QProcess stop emitting its signal. What the heck is wrong with it? Is this a Qt Bug? (5.7). Note this code is run parallel with QtConcurrent run. But this should not change anything, should it? Afaik sending signals in other threads is fine, though they will be queued.
QProcess *process = new QProcess;
process->setReadChannel(QProcess::StandardOutput);
connect(process, &QProcess::readyReadStandardOutput, [](){
qDebug()<< "readyReadStandardOutput";
});
connect(process, &QProcess::stateChanged, [](QProcess::ProcessState state){
qDebug()<< "stateChanged"<< state;
});
connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
[=](){
qDebug()<< "finsished";
});
connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
[this, process](int exitCode, QProcess::ExitStatus exitStatus){
qDebug()<< "finsished";
if (exitStatus == QProcess::NormalExit && exitCode == 0){
while (process->canReadLine()) {
QString line = QString::fromLocal8Bit(process->readLine());
QRegularExpression regex("\"(.*)\" {(.*)}");
QRegularExpressionMatch match = regex.match(line);
names_.push_back(match.captured(1));
uuids_.push_back(match.captured(2));
}
}
process->deleteLater();
});
process->start("VBoxManage", {"list", "vms"});
process->waitForFinished(); // This line changes everything
qDebug()<< "leftWaitForFinished";
You're not running an event loop in the thread where the QProcess instance lives. Any QObject in a thread without an event loop is only partially functional - timers won't run, queued calls won't be delivered, etc. So you can't do that. Using QObjects with QtConcurrent::run requires care.
At the very least, you should have a temporary event loop for as long as the process lives - in that case you should hold QProcess by value, since deleteLater won't be executed after the event loop has quit.
QProcess process;
...
QEventLoop loop;
connect(process, &QProcess::finished, &loop, &QEventLoop::quit);
loop.exec();
Otherwise, you need to keep the process in a more durable thread, and keep that thread handle (QThread is but a handle!) in a thread that has an event loop that can dispose of it when it's done.
// This can be run from a lambda that runs in an arbitrary thread
auto thread = new QThread;
auto process = new QProcess;
...
connect(process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
[this, process](int exitCode, QProcess::ExitStatus exitStatus){
...
process->deleteLater();
process->thread()->quit();
});
process->start("VBoxManage", {"list", "vms"});
process->moveToThread(thread);
// Move the thread **handle** to the main thread
thread->moveToThread(qApp->thread());
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
thread->start();
Alas, this is very silly since you're creating temporary threads and that's expensive and wasteful. You should have one additional worker thread where you take care of all low-overhead work such as QProcess interaction. That thread should always be running, and you can move all QProcess and similar object instances to it, from concurrent lambdas etc.
What is the corrent way to stop threads in Qt?
Suppose that I have a worker (LicenseChecker class) and I want to do some actions every n seconds in the process member function. I need to do it indefinitely, until someone abort my loop.
_worker = new LicenseChecker;
_thread = new QThread;
_worker->moveToThread(_thread);
connect(_thread, SIGNAL(started()), _worker, SLOT(process()));
connect(_worker, SIGNAL(finished()), _thread, SLOT(quit()));
connect(_worker, SIGNAL(finished()), _worker, SLOT(deleteLater()));
connect(_thread, SIGNAL(finished()), _thread, SLOT(deleteLater()));
connect(_worker, SIGNAL(newLicensesActivated(QVector<LicenseInfo>)),
this, SLOT(newLicensesActivated(QVector<LicenseInfo>)));
_thread->start();
What can I do to abort it?
The first idea that I came up with was to define the sleep function as the following:
bool LicenseChecker::sleep(int seconds)
{
QTime end_time = QTime::currentTime().addSecs(seconds);
while (QTime::currentTime() < end_time)
{
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
QMutexLocker lock(&_abort_sync);
if (_abort)
{
return false;
}
}
return true;
}
and to check the return code of this function in my infinite loop:
while (true)
{
if (!sleep(5))
{
emit finished();
return;
}
// ...
}
And then on MainWindow's close event I need to do the following:
_worker->stop();
_thread->wait();
where stop member function just sets _abort data member to true, but the application hangs on these calls.
What is the right way to accomplish such task?
Seems that you call stop() from another thread (the main thread) but the worker instance is located in the worker thread. You can overcome this problem by carefully writing something like this to invoke a method from another thread:
void Worker::stop()
{
// make thread safe
if(QThread::currentThread() != this->thread())
{
this->metaObject()->invokeMethod(this, "stop", Qt::QueuedConnection);
return;
}
REAL CODE HERE ...
}
Besides that, i would rather use a timer to fire every n seconds to do the licence check. Something like that (example of an database connection checker):
dbCheckerThread = new QThread(this);
dbCheckerTimer = new QTimer();
dbCheckerTimer->setInterval(CHECKDBCONNECTIONINTERVALL);
dbCheckerTimer->moveToThread(dbCheckerThread);
dbChecker->moveToThread(dbCheckerThread);
connect(dbCheckerTimer, &QTimer::timeout, dbChecker, &DbConnectionChecker::checkConnection);
connect(dbCheckerThread, &QThread::started, dbCheckerTimer, static_cast<void (QTimer::*)(void)>(&QTimer::start));
dbCheckerThread->start();
In the Worker Class I have two functions who works and control the thread, start() and abort()
void Worker::requestWork()
{
mutex.lock();
_working = true;
_abort = false;
qDebug()<<"Le thread travail de"<<this->myId<<" "<<thread()->currentThreadId();
mutex.unlock();
emit workRequested();
}
void Worker::abort()
{
mutex.lock();
if(_working) {
_abort = true;
qDebug()<<"Le thread "<<thread()->currentThreadId()<<" s'arrete";
}
mutex.unlock();
}
As you can see the workrequest emit a signal saying to the thread to start to work. And because the Worker Class is in the thread, how can I pause or restore it? From the worker class? from the MainWindow?
and now the Whole code.Mainwindow Class.
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
QObject::connect(ui->lineEdit,SIGNAL(textChanged(QString)),this,SLOT(command(QString)));
thread = new QThread();
worker = new Worker();
worker->moveToThread(thread);
connect(worker, SIGNAL(valueChanged(QString)), ui->label, SLOT(setText(QString)));
connect(worker, SIGNAL(workRequested()), thread, SLOT(start()));
connect(thread, SIGNAL(started()), worker, SLOT(doWork()));
connect(worker, SIGNAL(finished()), thread, SLOT(quit()), Qt::DirectConnection);
}
//QLineEdit send signal to slot "command" permit to control the thread
void MainWindow::command(QString text){
qDebug()<<"le message a bien ete intercepte";
qDebug()<<ui->lineEdit->text();
if (text.contains("help"))
qDebug()<<"heeeelp";
if (text.contains("pause")){
worker->Paused();
if(thread->isRunning()){}
//cond.wait()
}
if (text.contains("restart")){
worker->Restarted();
if (!thread->isRunning()){}
//cond.wakeAll();
}
if (text.contains("stopped")){
worker->Paused();
thread->wait();
}
if (text.contains("start")){
worker->requestWork();
}
if (text.contains("destroyed")){
worker->destroyed();
}
}
So my question is: how to pause and restart the thread from the command() slot when the User insert "Pause" ?
I think you mess up the two possible ways of doing work with QThread:
One is to have an object (Worker) moved to a start()ed QThread. This way you do work by calling slots on the Worker, the work is done when the slots execute. You don't pause/resume this - when the slot is done, QThread will wait for new work, the same way QApplication waits for events when it's idle.
Second way is subclassing QThread, reimplementing run() and creating (some sort of) Worker there. Using this way you must create your "work queue" with something similar to your state variables (_working, _done, wait conditions, etc), because the thread will exit if it leaves run(), you must pause/resume yourself.
I want to make a condition in code to wait until all threads are finished in the main thread final slot, following is the test code ..
testClass::testClass()
{
m_count = 0;
m_flag = false;
for( int i = 0; i < 3; i++)
{
QThread *thread = new QThread();
WorkerThread *worker = new WorkerThread();
connect(thread, SIGNAL(started()), worker, SLOT(startThread()));
connect(worker, SIGNAL(workerFinished()), this, SLOT(threadFinished()));
connect(worker, SIGNAL(workerFinished()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
m_workerList.append(worker);
}
}
void testClass::threadFinished()
{
// wait untill all thread are finished, how to achive this ?
if(!m_flag)
{
// print << m_flag;
m_flag = true;
}
}
void WorkerThread::startThread()
{
emit workerFinished();
}
here testClass is in main thread and I want the application to wait in threadFinished slot until all threads I started in testClass constructor to finish, could anyone suggest best way to do this ?
I am using Qt 5.4.0 in Windows 7.
If using boost in addition to Qt is an option, you can use a thread group and call join_all, which waits until all threads in the group are done.
Also, QThreadPool provides the waitForDone() function ("Waits for each thread to exit and removes all threads from the thread pool."), however you might have to restructure your worker objects slightly to comply to the QRunnable interface.
A more basic solution would be to set a QVector or QMap member variable with one entry for each thread and set them to true when the respective thread finishes. Continue program execution in your slot only when all threads have reached "finished = true" state in your member variable.
What I'm having is a strange problem in typical scenario: QTcpServer's method incomingConnection is overrided in custom class, and any received connection is planned for processing in separate thread on QThreadPool.
Server:
void FooS::incomingConnection(qintptr socketDescriptor)
{
QThreadPool *thread_pool = QThreadPool::globalInstance();
FooSocket *fs = new FooSocket();
fs->setSocket(socketDescriptor);
thread_pool->start(fs);
}
Task:
class FooSocket: public QObject, public QRunnable;
...
private slots:
void connectionIncomingData();
...
void FooSocket::run() {
QTcpSocket *socket = new QTcpSocket();
qDebug() << "SD: " << socketDescriptor; // is correct
if (!socket->setSocketDescriptor(socketDescriptor)) {
qDebug() << "Can't set socket descriptor";
emit error(socket->error());
return;
}
// -- had no effect here
// socket->moveToThread(QThread::currentThread());
connect(socket, SIGNAL(readyRead()), this, SLOT(connectionIncomingData()));
connect(socket, SIGNAL(disconnected()), this, SLOT(connectionClosed()));
}
readyRead signal doesn't gets triggered, but socket client is confirmed (tcpdump) to send data..
After making QRunnable to spawn a QThread object with socket logics inside, and toying with setAutoDelete, moveToThread - still no effect.
In order to process events in a QRunnable, a thread needs to have its own event loop, it mustn't rely on the one from the main thread. From what you've shown in your code, your thread quickly starts, then exits without running a loop.
Try adding
QEventLoop loop;
// connect a signal to the event loop's quit() slot
loop.exec();