Scenario
Lets say, I have a procedure called parallelRun. It would take a list of workers, each having a getWorkAmount():int, a run() method, a finished() signal and a cancel() slot:
void parallelRun( std::vector< Worker* > workers );
Its implementation should:
1. Open a QPogressDialog:
unsigned int totalWorkAmount = 0;
for( auto it = workers.begin(); it != workers.end(); ++it )
{
totalWorkAmount += ( **it ).getWorkAmount();
}
LoadUI ui( 0, totalWorkAmount, this );
with
class LoadUI : public QObject
{
Q_OBJECT
public:
LoadUI( int min, int max, QWidget* modalParent )
: totalProgres( 0 )
, progressDlg( "Working", "Abort", min, max, modalParent )
{
connect( &progressDlg, SIGNAL( canceled() ), this, SLOT( cancel() ) );
progressDlg.setWindowModality( Qt::WindowModal );
progressDlg.show();
}
bool wasCanceled() const
{
return progressDlg.wasCanceled();
}
public slots:
void progress( int amount )
{
totalProgres += amount;
progressDlg.setValue( totalProgres );
progressDlg.update();
QApplication::processEvents();
}
signals:
void canceled();
private slots:
void cancel()
{
emit canceled();
}
private:
int totalProgres;
QProgressDialog progressDlg;
}
2. Create one thread for each worker
std::vector< std::unique_ptr< QThread > > threads;
for( auto it = workers.begin(); it != workers.end(); ++it )
{
std::unique_ptr< QThread > thread( new QThread() );
Worker* const worker = *it;
worker->moveToThread( thread.get() );
QObject::connect( worker, SIGNAL( finished() ), thread.get(), SLOT( quit() ) );
QObject::connect( &ui, SIGNAL( canceled() ), worker, SLOT( cancel() ) );
QObject::connect( *it, SIGNAL( progressed( int ) ), &ui, SLOT( progress( int ) ) );
thread->start( priority );
threads.push_back( std::move( thread ) );
}
3. Run them simultaneously
for( auto it = workers.begin(); it != workers.end(); ++it )
{
QMetaObject::invokeMethod( *it, "run", Qt::QueuedConnection );
}
load() is run when the user clicks an UI-button.
Problem
How am I supposed to extend this code, if I want to make parallelRun block until all workers are finished, without freezing the QProgressDialog?
Deliberations
Using a barrier
I tried adding the following code at the end of the parallelRun routine:
QApplication::processEvents();
for( auto it = threads.begin(); it != threads.end(); ++it )
{
( **it ).wait();
}
The impact of this few lines of extra-code is, that LoadUI::progress is never entered, since the GUI-thread is asleep and therefore it's event loop isn't processed: In Qt, signals are delivered to slots by posting them to the event loop of the thread, associated to the object the slot belongs to. This is why the progressed signal of a worker is never delivered.
I think, the appropriate solution would be to run QApplication::processEvents() within the GUI-thread anytime a progressed signal is emitted by a worker. On the other hand, I guess this cannot be done, since the GUI-thread is asleep.
Another possible solution
Another possibility would be to use an active waiting-like solution:
for( auto it = threads.begin(); it != threads.end(); ++it )
{
while( ( **it ).isRunning() )
{
QApplication::processEvents();
}
}
for( auto it = threads.begin(); it != threads.end(); ++it )
{
( **it ).wait();
}
This also requires adding the following line of code right after thread->start( priority );:
while( !thread->isRunning() );
I don't think that this is a nice solution, but at least it works. How can this be done without the drawbacks of active waiting?
Thanks in advance!
You could use the threads' finished() signals to wait for them all to finish in the main GUI loop instead of using QApplication::processEvents. The progress dialog modality will ensure that only that dialog window is active until it is explicitly closed.
class WorkerManager : public QObject {
Q_OBJECT
private:
// to be able to access the threads and ui, they are defined as a members
std::vector<std::unique_ptr<QThread> > threads;
LoadUI *ui;
int finishedThreadCount;
public:
WorkerManager()
: finishedThreadCount(0)
{
// Open the QProgressDialog
...
// Create and start the threads
...
// Connect the finished() signal of each thread
// to the slot onThreadFinished
for( auto it = threads.begin(); it != threads.end(); ++it ) {
QObject::connect(
it->get(), SIGNAL(finished()),
this, SLOT(onThreadFinished()) );
}
}
private slots:
void onThreadFinished() {
++finishedThreadCount;
if(finishedThreadCount == threads.size())
{
// clean up the threads if necessary
// close the dialog
// and eventually destroy the object this itself
}
}
};
Or you can run a nested QEventLoop to wait for the threads to finish synchronously while still keeping the GUI responsive:
// Open the QProgressDialog
...
// Create and start the threads
...
// Create and run a local event loop,
// which will be interrupted each time a thread finishes
QEventLoop loop;
for( auto it = threads.begin(); it != threads.end(); ++it )
{
QObject::connect(
it->get(), SIGNAL(finished()),
&loop, SLOT(quit()) );
}
for(int i = 0, threadCount = threads.size(); i < threadCount; ++i)
loop.exec();
If the progress reach the maximum only when the work is completely done, you can use progressDlg->exec() instead of a QEventLoop which will block until the maximum is reached or until the user clicks on the "Cancel" button.
Instead of building your own. Maybe QThreadPool is what you are looking for?
QThreadPool has a function for waiting for all workers.
Related
I am trying to write thread poll with QThread.
class ThreadPool: public QObject
{
Q_OBJECT
public:
ThreadPool(int maxThreads);
void addTask(MyTask *task);
private:
int maxThreads;
QMutex mutex;
QVector<QPair<bool, QThread>> threads;
QThread *getFreeThread();
public slots:
void freeThread();
};
void ThreadPool::addTask(MyTask* task)
{
QThread *thread = getFreeThread();
task->moveToThread(thread);
connect(thread, SIGNAL(started()), task, SLOT(doWork()));
connect(task, SIGNAL(workFinished()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), task, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), this, SLOT(freeThread()));
thread->start();
}
I am creating a limited number of threads in which I want to perform tasks.
However, I do not understand how to get the number of the freed thread.
I know about QThreadPool and Qtconcurrent, but I dont want to use it.
Perhaps, it is worth noting at each thread in QPair's vector is it free or not.
you do not really need a QVector<QPair<bool, QThread>> to keep track of all the threads in your Pool, instead use a QList< QThread* > which holds only the pointers to the free threads.
private:
QList<QThread*> freeThreads; // only free threads
QList<QThread*> allThreads; // just to count the number of all threads
In the slot freeThread() use the sender() method from QObject to get the pointer of the signal sender, which in this case will be the QThread, that has become free
void ThreadPool::freeThread()
{
// get the pointer to the thread that sent the signal:
QObject* threadFreed = QObject::sender();
if( ! freeThreads.contains( threadFreed ) )
{
// save the thread pointer in list
freeThreads << threadFreed;
}
}
Finally getFreeThread() can look like this:
QThread* getFreeThread()
{
if( ! freeThreads.isEmpty() )
{
// take the first free thread
return freeThreads.takeFirst();
}
else
{
if(allThreads.size() < maxThreads )
{
// create a new thread
QThread* thread = new QThread(this);
allThreads << thread;
return thread;
}
else
{
// Maximum number of threads exceeded
// and no free thread is available
return NULL;
}
}
}
Also you should handle the case when a NULL pointer is returned in addTask:
void ThreadPool::addTask(MyTask* task)
{
QThread *thread = getFreeThread();
if( ! thread )
{
// do something else
return;
}
// proceed with thread execution ...
}
I am trying to determine if you can move one object instance to different threads at different points during run-time.
Below is some sample code to show you what I mean:
this->thread1 = new QThread( this );
this->thread2 = new QThread( this );
this->pObject->moveToThread( this->thread1 );
connect(this->thread1, SIGNAL(started()), this->pObject, SLOT(fnc1()));
connect(this->thread2, SIGNAL(started()), this->pObject, SLOT(fnc2()));
this->thread1->start();
//after thread1 has finished
this->pObject->moveToThread( this->thread2 );
this->thread2->start();
Is it possible to do this?
Edit: After Kuba's advice on not using a direct connection and him pointing out I must be interfering with the event loop somehow, I realised manually terminating the threads was not a good idea. I am adding my termination of the threads here to show where I am going wrong and to try and find a better way of achieving the same result.
connect(this->pObject, SIGNAL(finished()), this, SLOT(stopThread()));
void Class::stopThread( void )
{
if( this->thread1->isRunning() )
{
this->thread1->terminate();
return;
}
if( this->thread2->isRunning() )
{
this->thread2->terminate();
this->pObject->moveToThread( this->thread1 );
}
}
void Object::fnc1( void )
{
/*Does some work..*/
finished(); //Calls 'finished' to signal stopThread when done (not stopping on its own)
}
ADDITIONAL INFO
I have MainClass which holds the instances to both thread1, thread2 and pObject (pointer to the object I am trying to move from thread1 to thread2 and back again if necessary).
Main class constructor:
MainClass::MainClass( QWidget *parent ) : QMainWindow(parent)
{
this->ui.setupUi(this);
this->thread1 = new QThread( this );
this->thread2 = new QThread( this );
this->pObject->moveToThread( this->thread1 );
connect( this->pObject, SIGNAL(finished()), this, SLOT(stopThread()) );
connect( this->thread1, SIGNAL(started()), this->pObject, SLOT(fnc1()) );
connect( this->thread2, SIGNAL(started()), this->pObject, SLOT(fnc2()) );
}
Slot for when menu item is clicked:
void MainClass::on_action_call_fnc1_triggered( void )
{
if( this->thread1->isRunning() )
return;
/*EXECUTES SOME CLASSIFIED CODE THAT CANNOT BE SHOWN*/
this->thread1->start();//should trigger fnc1 execution
}
fnc1 held in Object class that is called when thread1 starts:
void Object::fnc1( void )
{
/*DOES SOME MORE SECRET PROCESSING THAT CANNOT BE SHOWN*/
this->finished(); // triggers MainClass::stopThread( void )
}
Slot for when menu item is clicked to start fnc2 executing:
void MainClass::on_action_call_fnc2_triggered( void )
{
if( this->thread1->isRunning() || this->thread2->isRunning() )
return;
this->pObject->moveToThread( this->thread2 );
this->thread2->start();//should trigger fnc2 execution
}
fnc2 held in Object:
void Object::fnc2( void )
{
/*DOES SOME MORE SECRET PROCESSING THAT CANNOT BE SHOWN*/
this->finished(); // triggers MainClass::stopThread( void )
}
stopThread function:
void MainClass::stopThread( void )
{
if( this->thread1->isRunning() )
{
/*this->thread1->quit();
this->thread1->exit();*/
this->thread1->terminate();
//this->thread1->wait( 0 ); //Trying different ways of stopping the thread
return;
}
if( this->thread2->isRunning() )
{
this->thread2->terminate();
//this->thread2->quit(); // Trying different ways of stopping the thread
//this->thread2->exit();
this->pObject->moveToThread( this->thread1 );
}
}
It will work, but you have to assert that the object's thread is indeed finished:
Q_ASSERT(pObject->thread() == nullptr);
pObject->moveToThread(thread2);
When thread1 is finished, the object's thread becomes null and only then you're allowed to move it to another thread from arbitrary thread. Otherwise, if object's thread is not finished yet, you could only move the object from its thread:
QTimer::singleShot(0, pObject, [this]{ pObject->moveToThread(thread2); }
I am implementing client side of TCP-Ip server based on Qt but the program crashes when I closed connection and start again by hitting connect button.
Overview of project. I have Qwidget ( main application) with 2 lineEdit for user to input port number and server Ip address.
It also has 2 button that connect to server and disconnect. On hitting connect button , it will call constructor of client socket and call connectToHost.
Testing: Tried testing on same computer with server running on port 6000 and ip address 127.0.0.1.
Problem: When I launch Client application.And input port number and address and hit connect button it connects successfully. And I can write to server successfully. then I click disconnect button and it disconnects successfully but after that if I connect again by clicking connect button it crashes. I know problem is with tcpSocket but have no idea how to fix it.
clientAgent( QWidget appilciation ).h
namespace Ui {
class ClientAgent;
}
class ClientAgent : public QWidget
{
Q_OBJECT
public:
explicit ClientAgent(QWidget *parent = 0);
~ClientAgent();
private:
Ui::ClientAgent *ui;
ClientForTest *tcpSockForTest;
// Qwidget declartion
below
....
private slots:
void startClient();
void stopClient();
public slots:
void getCliReqTextChanged();
};
ClientAgent.cpp
ClientAgent::ClientAgent( QWidget *parent ) :
QWidget(parent),
ui( new Ui::ClientAgent )
{
// Widget declartion and initilisation
// Layout design etc
//define signals and slots for Client
// signals and slots
connect( btnStartClient, SIGNAL( clicked() ), this, SLOT( startClient() ) );
connect( btnStopClient, SIGNAL( clicked() ), this, SLOT( stopClient() ) );
// for write
connect( lEditCliReq, SIGNAL( textChanged(const QString& ) ), this, SLOT( getCliReqTextChanged() ) );
ui->setupUi( this );
}
ClientAgent::~ClientAgent()
{
delete ui;
}
void ClientAgent::startClient()
{
qDebug() << " we are in strt Clinet";
qDebug() << " connecting ";
// get server address and port number from GUI
QString testAddr = lEditAddr->text();
QString testPort = lEditPrt->text();
tcpSockForTest = new ClientForTest( testAddr, testPort.toInt(), this );
if( tcpSockForTest->connectToServer() == true )
{
lblCliStatus->setText(" connected ....");
}
else
{
lblCliStatus->setText(" failed to connect....");
}
}
void ClientAgent::stopClient()
{
qDebug() << " disconnecting ";
tcpSockForTest->disconnectToServer();
delete tcpSockForTest;
}
ClientForTest.h
class ClientForTest : public QObject
{
Q_OBJECT
public:
ClientForTest( QString hostAddr, quint16 portNum, QObject *parent );
bool connectToServer();
void disconnectToServer();
QString getStoreMsgFrmCliReq( ) const;
void writeToTest(const QString& stripCmd );
void executeSignals();
~ClientForTest();
signals:
void sigSendData();
public slots:
void connectedToTest();
void connectionClosedByServer();
void error();
private:
QTcpSocket *sockFortest;
QString hostName;
quint16 portNumber;
quint16 nextBlockSize;
QString storeLineEditMsg;// store message from lineEditCliReq;
};
ClientForTest.cpp
ClientForTest::ClientForTest( QString hostAddr, quint16 portNum, QObject *parent ) :
hostName( hostAddr ),
portNumber( portNum ),
QObject( parent )
{
connect( sockFortest, SIGNAL( connected() ), this, SLOT( connectedToTest() ) );
connect( sockFortest, SIGNAL( disconnected() ), this, SLOT( connectionClosedByServer() ) );
connect( sockFortest, SIGNAL( error( QAbstractSocket::SocketError ) ), this, SLOT( error() ) );
storeLineEditMsg = "";
}
void ClientForTest::executeSignals()
{
// actually I need toplace itin constructor will do later on
connect( this, SIGNAL( sigSendData() ), this, SLOT( connectedToTest() ) );
}
ClientForTest::~ClientForTest()
{
sockFortest->close();
delete sockFortest;
}
bool ClientForTest::connectToServer()
{
sockFortest = new QTcpSocket( this->parent() ); // COULD be Probelm here but how to fix it?
sockFortest->connectToHost( hostName, portNumber );
nextBlockSize = 0;
executeSignals();
return sockFortest->waitForConnected(1000);
}
void ClientForTest::disconnectToServer()
{
sockFortest->close();
//sockFortest.close();
qDebug() << "in disconnect for Test ";
emit updateLabelinParent( updateStatusDis );
}
void ClientForTest::connectionClosedByServer()
{
qDebug() << "connection closed by server ";
if( nextBlockSize != 0xFFFF )
{
disconnectToServer();
}
}
void ClientForTest::error()
{
qDebug() << "in error ";
disconnectToServer();
}
void ClientForTest::writeToTest( const QString& stripCmd )
{
storeLineEditMsg = stripCmd;
if( sockFortest->state() == QTcpSocket::UnconnectedState )
{
sockFortest->close();
delete sockFortest;
sockFortest = new QTcpSocket( this->parent() );
if( connectToServer() == true )
{
qDebug() << " YEEEE CONNECTED AGAIN finally ";
}
}
if( sockFortest->state() == QTcpSocket::ConnectedState )
{
qDebug() << " YEEEE CONNECTED";
sigSendData();
}
}
void ClientForTest::connectedToTest( )
{
QByteArray block;
QDataStream out( &block, QIODevice::WriteOnly );
out.setVersion( QDataStream::Qt_4_3 );
QString stripCmd = getStoreMsgFrmCliReq(); // received info for some other function I havnt shown that func here
out << quint16( 0 ) << stripCmd;
out.device()->seek( 0 );
out << quint16( block.size() - sizeof( quint16 ) );
qDebug()<<" yeeeee connected state...";
sockFortest->write( block );
//reset storeLineEditMessage
storeLineEditMsg.clear();
}
A few things, that could all together be responsible for your crash:
you connect the signals inside the ClientForTest constructor, but the socket itself is created later. That won't work. Move the line sockFortest = new QTcpSocket(this); to the constructor, before connecting
Same line, use this instead of this->parent()
And finally: There are multiple places you create/delete the socket. Don't do it. Create the socket inside the constructor with this as parent, and that's it. In your connectToServer, establish the connection, and in disconnectToServer, close it using disconnectFromHost. Same goes for the startClient and stopClient. Create the object once and just use the connect/disconnect functions, no deleting.
If code is required, I can add some.
Since it was requested, here is some more explanation:
Of course you can use parent, but in your case, the ClientForTest is an QObject, too. If you set the ClientForTest as as parent of the socket and the widget as the parent of ClientForTest, they will be both cleaned up properly. If you use this->parent(), both will be destroyed "at the same time". However, one comes first and somtimes Qt changes the order, so your socket could be destroyed before the ClientForTest. The destructor of ClientForTest would crash. That won't happen if the socket is a child of the ClientForTest
The main difference between close() and disconnectFromHost() is that the first actually closes the OS socket, while the second does not. The problem is, after a socket was closed, you cannot use it to create a new connection. Thus, if you want to reuse the socket, use disconnectFromHost() otherwise close()
And regarding 3. and 4.:
What you are doing is creating the ClientForTest when the user clicks connect, and delete it as soon as he clicks disconnect. But thats no good design (IMHO). Since tcpSockForTest already is a member of the class, create it (via new) inside the constructor, and delete it in the destructor (optionally, because if you pass the widget as parent, Qt will delete it for you).
I have discovered that even a simple wait on QMutex will cause assertion. What am I possibly doing wrong?
QMutex mutex;
SyncMgr::SyncMgr(QObject *parent) : QObject(parent)
{
moveToThread( &thread );
thread.start();
process = new QProcess( this);
connect( process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadyReadStandardOutput() ) );
connect( process, SIGNAL(readyReadStandardError()), this, SLOT(onReadyReadStandardError() ) );
}
SyncMgr::~SyncMgr()
{
delete process;
}
void SyncMgr::onConnected()
{
cmdDispatcher.sendGetSerialNo();
// this asserts
waitForResponse.wait( &mutex ); // waitForResponse is CWaitCondition object
// ...
}
I get assert and the error message is:
ASSERT:'copy' in the thread\qmutex.cpp, line 525
You need to lock the mutex before calling waitForResponse.wait().
The SyncMgr::onConnected() method should look like this:
void SyncMgr::onConnected()
{
cmdDispatcher.sendGetSerialNo();
mutex.lock();
waitForResponse.wait( &mutex );
// do something
mutex.unlock();
...
}
You can find more information here:
http://doc.qt.io/qt-5/qwaitcondition.html#wait
Basically I'm calling a function from another thread using QtConcurrent.
Working as expected but once I create a QInputDialog within the called function, I'm getting an assertion exception telling me that I have to create the Dialog in the Main GUI Thread.
To be more specific this line:
password = QInputDialog::getText( this , tr( "Password" ) , tr( "Enter Password:" ) , QLineEdit::Password , selectedPassword , &ok );
Now the question would be how can I call the Dialog from a new thread without too much extra work.
You can't create widgets outside from main thread. You can emit signal from network thread and create dialog in main thread.
Or do something like this (pseudo-code):
class NotificationManager : public QObject
{
Q_OBJECT
//...
public slots:
void showMessage( const QString& text )
{
if ( QThread::currendThread() != this->thread() )
{
QMetaObject::invoke( this, "showMessage", Qt::QueuedConnection, Q_ARG( QString, text );
// Or use Qt::BlockingQueuedConnection to freeze caller thread, until dialog will be closed
return;
}
QMessageBox::information( nullptr, QString(), text );
}
};
class ThreadedWorker : public QRunnable
{
ThreadedWorker( NotificationManager *notifications )
: _notifications( notifications )
{}
void run() override
{
// Do some work;
notifications->showMessage( "Show this in GUI thread" );
}
private:
NotificationManager *_notifications;
}