PDA

View Full Version : How can i get notification obout thread deleting ?



Pechkin
14th March 2014, 13:17
I wrote a code of creation thread like this example http://mayaposch.wordpress.com/2011/11/0…ll-explanation/
I only added two signal/slot connections:
connect(clientthread, SIGNAL(destroyed(QObject* )), this, SLOT(onDestroyCT(QObject* )));
connect(CT, SIGNAL(destroyed(QObject* )), this, SLOT(onDestroyCST(QObject* )));
But they doesn't work. Could anybody advice me, why those connections doesn't work and i can't get notification of deletion of objects ?
code below:


// ================= clientsocket.h ==================
class ClientSocketThread : public QObject
{
Q_OBJECT
public:
ClientSocketThread(QObject * parent = 0);
~ClientSocketThread();
private:
volatile bool stopped;
QObject * MW;

public slots:
void process();
void stop();

signals:
void finished();

};
// =========== mainwindow.cpp ======================
void MainWindow::CreateClientThread()
{
isDeleted = 0;
CT = new ClientSocketThread(this);
clientthread = new QThread(this);
CT->moveToThread(clientthread);

connect(clientthread, SIGNAL(destroyed(QObject* )), this, SLOT(onDestroyCT(QObject* )));
connect(CT, SIGNAL(destroyed(QObject* )), this, SLOT(onDestroyCST(QObject* )));

connect(clientthread, SIGNAL(started()), CT, SLOT(process()));
connect(CT, SIGNAL(finished()), clientthread, SLOT(quit()));
connect(CT, SIGNAL(finished()), CT, SLOT(deleteLater()));
connect(clientthread, SIGNAL(finished()), clientthread, SLOT(deleteLater()));

clientthread->start();
}

void MainWindow::DeleteClientThread()
{
CT->stop();
while(!(isDeleted == 2));
}

void MainWindow::onDestroyCT(QObject* obj)
{
isDeleted++;
}

void MainWindow::onDestroyCST(QObject* obj)
{
isDeleted++;
}
// ========================= clientsocket.cpp ===========================
ClientSocketThread::ClientSocketThread(QObject * parent)
{
MW = parent;
stopped = false;
ready = false;
}

ClientSocketThread::~ClientSocketThread()
{
//
}


void ClientSocketThread::stop()
{
stopped = true;
}

void ClientSocketThread::update()
{
ready = false;
}

void ClientSocketThread::_run()
{
while (!stopped)
{

}
}

void ClientSocketThread::process()
{
QString infostr = "hello !!!";
while (!stopped)
{
while (!stopped);
QCoreApplication::postEvent(MW,new TS_SocketEvent(SocketEventDebug,infostr));
}
emit finished();
}

Lesiok
14th March 2014, 14:50
Because loop while(!(isDeleted == 2)) blocks event loop.

Pechkin
14th March 2014, 15:59
Thanks,that sounds right, because this method is called by button's clicking. But How can I unblock eventloop and create a new event for button's clicking (at the end of queue of events) or pass another events while "while(!(isDeleted == 2))" is waiting in order to process destroyed() signal ? Of caurse, I can create special thread for this case but i don't think that is good idea. May be, Do you advice me an own approach for this issue ?

anda_skoa
15th March 2014, 15:30
Just don't execute this loop.
If you want to do anything when isDeleted reaches 2 then do that in the slots where it is incremented

Cheers,
_

Pechkin
15th March 2014, 19:13
Just don't execute this loop.
If you want to do anything when isDeleted reaches 2 then do that in the slots where it is incremented

Cheers,
_
The Aim is execution of complete action of deletion of thread in button's click event handler. Of caurse i can temporary make inactive button to a call of slot of destroeyed() signal but i'm not sure that is good idea. maybe if i call processEvents() in button click event handler i'll reach my aim but i'm not sure.

anda_skoa
15th March 2014, 21:47
If you call processEvents that is essentially the same as not doing the loop.

Why do you need to block the UI until the thread is deleted?

Cheers,
_

Pechkin
16th March 2014, 09:37
If you call processEvents that is essentially the same as not doing the loop.

Why do you need to block the UI until the thread is deleted?

Cheers,
_
In order to restore order. Otherwise the process of creation threads theoretically may outrun the process of deletion object and as conclusion
1)the overflow of queue of events or
2)overflow of heap because of many stubs of non-completly deleted threads
What situation more possible depend on internal architecture qt.

anda_skoa
16th March 2014, 09:47
You might want to look into reusing the thread or using a thread pool.

Anyway, how about showing a modal progress dialog?

Prohibits user interaction with the main UI, continues to run event processing, makes it clear to the user that the program is waiting for seomthing, can be made to not appear if the thread ends quickly enough.

Cheers,
_

Pechkin
16th March 2014, 13:37
I've stopped on this solving:


// mainwindow.h ================================================== ==================
void MainWindow::on_pushButton_Create_clicked()
{
pth = new TestThread(this);
clientthread = new QThread(this);
pth->moveToThread(clientthread);
//connect(clientthread, SIGNAL(destroyed(QObject* )), this, SLOT(onDestroyCT(QObject* )));
//connect(CT, SIGNAL(destroyed(QObject* )), this, SLOT(onDestroyCST(QObject* )));
connect(clientthread, SIGNAL(started()), pth, SLOT(process()));
//connect(CT, SIGNAL(finished()), clientthread, SLOT(quit()));
//connect(CT, SIGNAL(finished()), CT, SLOT(deleteLater()));
//connect(clientthread, SIGNAL(finished()), clientthread, SLOT(deleteLater()));
clientthread->start();
ui->pushButton_Create->setDisabled(true);
ui->pushButton_Delete->setEnabled(true);
}

void MainWindow::on_pushButton_Delete_clicked()
{
pth->stop();
while(!pth->isStopped);
clientthread->terminate();
//while(!clientthread->isRunning()); // why doesn't work ???
delete pth;
delete clientthread;
ui->pushButton_Create->setEnabled(true);
ui->pushButton_Delete->setDisabled(true);
}
// thread.h ================================================== =========================
class TestThread : public QObject
{
Q_OBJECT
public:
explicit TestThread(QObject * parent);
volatile bool isStopped;

private:
volatile bool isStopping;
QObject * MW;
public slots:
void process();
void stop();
};
// thread.cpp ================================================== ===================
TestThread::TestThread(QObject * parent) : MW(parent)
{
isStopped = false;
isStopping = false;
}

void TestThread::stop()
{
isStopping = true;
}

void TestThread::process()
{
QString infostr = "hello !!!";
while (!isStopping)
{
while (!isStopping);
QCoreApplication::postEvent(MW,new TestEvent(TestEventDebug,""));
}
isStopped = true;
}


It seems problem is solved but why doesn't work "while(!clientthread->isRunning());" ???

anda_skoa
16th March 2014, 15:08
You are accessing two variables from two threads without protecting them.

Do not call terminate().
Call quit().

join() the thread to wait for its exit.

Bllocking the UI is usually considered a very bad thing.

Cheers,
_

Pechkin
16th March 2014, 15:43
You are accessing two variables from two threads without protecting them.
_
Qt have properties's construction like Delphi, but i haven't learnt how use it yet. I corrected code a little bit:


class TestThread : public QObject
{
Q_OBJECT
public:
explicit TestThread(QObject * parent);
~TestThread();
QEventLoop * ploop;
bool GetStopState() { return isStopped; } // <---------- property
private:
volatile bool isStopping;
volatile bool isStopped;
volatile bool ready;
QObject * MW;
uint timerid;
QTimer * ptimer;
protected:
void timerEvent(QTimerEvent *event);
public slots:
void process();
void stop();
void update();
};



void MainWindow::on_pushButton_Delete_clicked()
{
pth->stop();
while(!pth->GetStopState());
clientthread->quit();
//while(!clientthread->isRunning());
delete pth;
delete clientthread;
ui->pushButton_Create->setEnabled(true);
ui->pushButton_Delete->setDisabled(true);
}

For the rest i haven't found a problem because for each variable write access from one thread and read access from one thread.



Do not call terminate().
Call quit().
_
Thank's Really it's true;


You are accessing two variables from two threads without protecting them.
join() the thread to wait for its exit.
Cheers,
_
Excuse me. I haven't caught this. What is "join()" ?


Bllocking the UI is usually considered a very bad thing.
Cheers,
_
I agree :)

anda_skoa
16th March 2014, 18:04
Protecting access


class Test : public QObject
{
public:
Test() : m_stop(false), m_isStopped(false) {}

void stop()
{
QMutexLocker locker(&m_mutex);
m_stop = true;
}

bool isStopped() const
{
QMutexLocker locker(&m_mutex);
return m_isStopped;
}

private:
bool shouldStop() const
{
QMutexLocker locker(&m_mutex);
return m_stop;
}

void hasStopped()
{
QMutexLocker locker(&m_mutex);
m_isStopped = true;
}

void process()
{
while (!shouldStop()) {
}
hasStopped();
}

private:
mutable QMutex;
};


And with join() I was referring to QThread::join()

Cheers,
_

Pechkin
17th March 2014, 12:30
Protecting access


class Test : public QObject
{
public:
Test() : m_stop(false), m_isStopped(false) {}

void stop()
{
QMutexLocker locker(&m_mutex);
m_stop = true;
}

bool isStopped() const
{
QMutexLocker locker(&m_mutex);
return m_isStopped;
}

private:
bool shouldStop() const
{
QMutexLocker locker(&m_mutex);
return m_stop;
}

void hasStopped()
{
QMutexLocker locker(&m_mutex);
m_isStopped = true;
}

void process()
{
while (!shouldStop()) {
}
hasStopped();
}

private:
mutable QMutex;
};


And with join() I was referring to QThread::join()

Cheers,
_
Thank's It's excelent example. But, I can't understand, What reason of use of mutex ? What risk it protects from ? Especially in constant methods.
I haven't found join() method in QThread class documentation (QT 5.2).

anda_skoa
17th March 2014, 13:14
Thank's It's excelent example. But, I can't understand, What reason of use of mutex ?

The mutex ensures that only one thread can access the protected variables at the same time.


What risk it protects from ?

Aside from it being very sloppy to have code that depends on chance, it ensures correct value propagation and value consistency.


Especially in constant methods.

Whether or not a method is constant doesn't matter. I just made the "readers" const because that is common practise.



I haven't found join() method in QThread class documentation (QT 5.2).

Sorry, my bad, it is called wait().
It is called join() in C++11's thread and I mixed that up :)

Cheers,
_

Pechkin
17th March 2014, 14:01
The mutex ensures that only one thread can access the protected variables at the same time.
Aside from it being very sloppy to have code that depends on chance, it ensures correct value propagation and value consistency.
Whether or not a method is constant doesn't matter. I just made the "readers" const because that is common practise.
Cheers,
_
Thank's, I know why variables need mutex. :)
May be, generally, the using of protected by mutex variables, is good style of programming, but what reason does use it here ? My target is program, which should compiles with qt 3.x and works under 16 MB of RAM & 200MhZ of CPU so i try to avoid using unnecessary code . If doesn't exist real risk of conflict access in current situation i'll prefer don't use it.

anda_skoa
17th March 2014, 15:27
As I said, the mutex is about guaranteeing certain things. If you can live with uncertainty then this is your choice.

My assumption was that you wanted to be sure the values are propagated correctly since you are blocking your UI.

Undefined behavior can be really nasty to debug.

Cheers,
_

Pechkin
17th March 2014, 15:58
As I said, the mutex is about guaranteeing certain things. If you can live with uncertainty then this is your choice.

My assumption was that you wanted to be sure the values are propagated correctly since you are blocking your UI.

Undefined behavior can be really nasty to debug.

Cheers,
_
You are right.
My program uses 2 threads: main thread and this thread( thread2). m_stop has "write only" access from main thread and "read only" access from thread2.
m_isStopped has "read only access" from main thread and "write only" access from thread2. i'll want to be sure that undefined behavior is impossible if i don't use mutex. I'll be appreciated if you explain me why case of UB is real for current case.

anda_skoa
17th March 2014, 20:56
In your specific case you could be lucky, because the two writes are in a definite order, each true value is only read once and the value after that doesn't matter anymore.

In general you don't have to nice coinicdences. Each write will result in a memory write of an area that is as wide as the bus between CPU and memory. To alter only the one byte of the boolean (assuming the compiler doesn't even do more packing), the CPU will need to read the word, change one the bytes and then write the word back.
You do not want it to write anything else into the remaining bytes just because it is now executing the write of a different thread.

On top of that, in a system with multiple CPUs, you would probably like to ensure that the changed value is actually written to the memory and not just in the cache of the CPU executing the writing thread.

See https://en.wikipedia.org/wiki/Memory_barrier

Cheers,
_