[SOLVED] run a multithreaded program on a single core machine
-
Hi all,
i have a program in which i use QThreads and it runs perfectly on multicore machines. however i have a VM which has only one CPU core and when i run the application the cpu usage gets to 100%!!! and then i have to restart the machine.in my application when the program begins, 3 threads will be started but one of them runs in a while loop and waiting(blocking) for user reactions, and it only gets finished when the application quits. so in my application lifetime i always have 2 threads running(main thread, and the while loop thread) and i will start new threads from the main thread as needed.
-
Hi,
When a multithreaded program behaves differently on different machines, it's very possible that the program has a "race condition":http://stackoverflow.com/questions/34510/what-is-a-race-condition. Please show us your thread code.
It is definitely possible to run a multithreaded program on a single core CPU, but you should follow good design patterns. In Qt, you should process GUI reactions on the main thread, using signals and slots (do not use a separate thread to wait for user reactions). Have you used signals and slots before?
-
now that you mentioned race condition i think you are right becuase i'm using the same QMutex object for locking the threads! i think multiple threads try to access Qmutex and the race begins!
here is my code:@
wroker class header:
class wroker : public QObject
{
Q_OBJECT
public:
QMutex m_mutex;
// isFinished: slot event
bool isFinished;
}wroker class cpp:
Worker::Worker(QObject *parent) :
QObject(parent),
m_mutex(QMutex::Recursive)
{}
// one thread will run this function and this is executed in a while loop /// until program is closed
void worker::waitForSlotEvent()
{
while (!isFinished)
{
// WAIT FOR USB EVENT, ex: MASS STORAGE FLASH INSERTION/REMOVAL
.
.
.
emit slotEvent();
}
}int Worker::getData()
{
QMutexLocker mutexLocker(&m_mutex);
.
.
.emit getDataFinished(returnValue);
}int Worker::getAllConns()
{
QMutexLocker mutexLocker(&m_mutex);
.
.
.emit getConnFinished(returnValue);
}
here is Mainwindow header:
class Mainwindow : public QDialog {
Q_OBJECT
public:
QThread thrSlotEvent;
Worker objSlotEvent;
QThread thrData;
Worker objData;
Worker objConn;
QThread thrConn;
}here is Mainwindow cpp:
// app begins from here
void Mainwindow::initApp()
{
.
.
.
objData.moveToThread(&thrData);
QObject::connect(&thrData, SIGNAL(started()), &objData, SLOT(getData()));
QObject::connect(&objData, SIGNAL(getDataFinished(int)), this, SLOT(updateData(int)));
thrData.start();
.
.
.
}void Mainwindow::updateData(int returnValue)
{
// RUN SLOT EVENT DETECTION
startSlotEvent();//CHECK FOR ERRORS if(returnValue) { msg(returnValue); return; } // START THREAD Conn QObject::disconnect(&objConn, 0, 0, 0); QObject::disconnect(&thrConn, 0, 0, 0); objConn.moveToThread(&thrConn); QObject::connect(&thrConn, SIGNAL(started()), &objConn, SLOT(getAllConns())); QObject::connect(&objConn, SIGNAL(getConnFinished(int)), this, SLOT(getConnFinished(int))); thrConn.start();
}
void Mainwindow::startSlotEvent()
{
objSlotEvent.moveToThread(&thrSlotEvent);
objSlotEvent.isFinished = false;
QObject::connect(&thrSlotEvent, SIGNAL(started()), &objSlotEvent, SLOT(waitForSlotEvent()));
QObject::connect(&objSlotEvent, SIGNAL(slotEvent()), this, SLOT(refreshList()));
thrSlotEvent.start();
}void Mainwindow::refreshList()
{
// here i update a QTreeView
}}
@ -
What is the mutex for? I can't see any multi-thread access.
Why do you disconnect and re-connect signals in MainWindow::updateData()?
What is objConnCert (Line #95)? Why do you move it every time MainWindow::updateData() is called?
objSlotEvent, objData and objConn do 3 different jobs. Why are they instantiated from the same class?
You should not use a blocking while loop (line #22) when the thread has an event loop. Use a QTimer instead to call a slot at regular intervals.
-
[quote author="JKSH" date="1386052604"]# What is the mutex for? I can't see any multi-thread access.
because i haven't copied and paste the whole code in here!
Why do you disconnect and re-connect signals in MainWindow::updateData()?
What is objConnCert (Line #95)? Why do you move it every time
MainWindow::updateData() is called?
- objConnCert was a type mistake sorry for that!
- yes you are right i must move it in the constructor once
objSlotEvent, objData and objConn do 3 different jobs. Why are they instantiated from the same class?
the Mainwindow class is where my application starts from and as it starts i need 3 threads to start, these threads all run a different function in Worker class.
You should not use a blocking while loop (line #22) when the thread has an event loop. Use a QTimer instead to call a slot at regular intervals.
i will replace this and notify you.
thanks for your help :-)
[/quote] -
here is my updated code:
@
// wroker class header:
class wroker : public QObject
{
Q_OBJECT
public:
QMutex m_mutex;
// isFinished: slot event
bool isFinished;
}// wroker class cpp:
Worker::Worker(QObject *parent) :
QObject(parent),
m_mutex(QMutex::Recursive)
{}
// one thread will run this function and this is executed in a while loop /// until program is closed
void worker::waitForSlotEvent()
{
// no using while loop anymore, instead i call the function recursively
// while (!isFinished)
// {
// WAIT FOR USB EVENT, ex: MASS STORAGE FLASH INSERTION/REMOVAL
.
.
.emit slotEvent(); waitForSlotEvent(); // recursive call
// }
}int Worker::getData()
{
QMutexLocker mutexLocker(&m_mutex);
.
.
.emit getDataFinished(returnValue);
}
int Worker::getAllConns()
{
QMutexLocker mutexLocker(&m_mutex);
.
.
.emit getConnFinished(returnValue);
}
// here is Mainwindow header:
class Mainwindow : public QDialog
{
Q_OBJECT
public:
QThread thrSlotEvent;
Worker objSlotEvent;
QThread thrData;
Worker objData;
Worker objConn;
QThread thrConn;
}Mainwindow::Mainwindow() :
QDialog()
{
// ================================<< THREAD DATA >>================================
objData.moveToThread(&thrData);
QObject::connect(&thrData, SIGNAL(started()), &objData, SLOT(getConns()));
QObject::connect(&objData, SIGNAL(getDataFinished(int)), this, SLOT(updateData(int)));// ================================<< THREAD CONN >>================================ objConn.moveToThread(&thrConn); QObject::connect(&thrConn, SIGNAL(started()), &objConn, SLOT(getAllConns())); QObject::connect(&objConn, SIGNAL(getConnFinished(int)), this, SLOT(getConnFinished(int))); // ================================<< THREAD SLOT EVENT >>================================ objSlotEvent.moveToThread(&thrSlotEvent); QObject::connect(&thrSlotEvent, SIGNAL(started()), &objSlotEvent, SLOT(waitForSlotEvent())); QObject::connect(&objSlotEvent, SIGNAL(slotEvent()), this, SLOT(refreshList()));
}
void Mainwindow::initApp()
{
// =================================<< START THREAD DATA >>=================================
// some code here
thrData.start();
// some code here
}void Mainwindow::updateData(int returnValue)
{
// ================================<< RUN SLOT EVENT DETECTION >>================================
waitForSlotEvent(false);// ================================<< CHECK FOR ERRORS >>================================ if(returnValue) { // delete all threads getConnFinished(returnValue); return; } // ================================<< START THREAD CONN >>================================ thrConn.start(); // ================================<< REFRESH THE LIST >>================================ this->refreshList();
}
void Mainwindow::waitForSlotEvent(bool isFinished)
{
if(isFinished)
{
objSlotEvent.isFinished = true;
thrSlotEvent.quit();
thrSlotEvent.deleteLater();
objSlotEvent.deleteLater();
return;
}objSlotEvent.isFinished = false; thrSlotEvent.start();
}
void Mainwindow::getConnFinished(int returnValue)
{
// ================================<< CHECK FOR RETURN VALUE >>================================
if(returnValue)
{
Message::errNoTOerrStr(this, returnValue);
}// ================================<< QUIT THREAD CONN>>================================ thrConn.quit(); thrConn.exit(0); // ================================<< QUIT THREAD DATA >>================================ thrData.quit(); thrData.exit(0);
}
void Mainwindow::refreshList()
{
// update QTreeView
}@
-
i shouldn't have blocked for slot event! instead i used sleep(), although the problem is not completely solved and the CPU usage is still high.
-
Hi,
Sorry for the late reply. I'm still a bit unsure about your architecture. Usually, developers choose between two architectures:
- The traditional, low level way:
- Subclass QThread
- Implement an infinite while loop. Use sleep() to lower CPU usage.
- Pass data between threads using shared variables, guarded by with mutexes/wait-conditions
- Stop the thread by setting a "quit" flag, which breaks the infinite while loop.
- The high-level, Qt way:
- Don't subclass QThread
- Use an event loop, which automatically lowers CPU usage when there are no events/signals to process (no infinite-while-loop, no sleep())
- Pass data between threads using signals + slots. No locking is required.
- Stop the thread by invoking QThread::quit(), which stops the event loop.
It sounds like you've combined both methods. Can you choose one instead? I think that will make your code work better.
-
[quote author="JKSH" date="1386946314"]Hi,
Sorry for the late reply. I'm still a bit unsure about your architecture. Usually, developers choose between two architectures:
- The traditional, low level way:
- Subclass QThread
- Implement an infinite while loop. Use sleep() to lower CPU usage.
- Pass data between threads using shared variables, guarded by with mutexes/wait-conditions
- Stop the thread by setting a "quit" flag, which breaks the infinite while loop.
- The high-level, Qt way:
- Don't subclass QThread
- Use an event loop, which automatically lowers CPU usage when there are no events/signals to process (no infinite-while-loop, no sleep())
- Pass data between threads using signals + slots. No locking is required.
- Stop the thread by invoking QThread::quit(), which stops the event loop.
It sounds like you've combined both methods. Can you choose one instead? I think that will make your code work better.[/quote]
Thanks for your reply :-)
to be honest, i'm a newbie in qt and also Threads. actually i want to implement my code like the 2nd approach you just mentioned.
would you mind providing me with an example which is using threads inside an event loop(i don't know how to use event loops :-( ).and by the way, you mentioned no locking is required when using signals and slots. would you explain a little more?
-
[quote author="JKSH" date="1386946314"]Hi,
Sorry for the late reply. I'm still a bit unsure about your architecture. Usually, developers choose between two architectures:
- The traditional, low level way:
- Subclass QThread
- Implement an infinite while loop. Use sleep() to lower CPU usage.
- Pass data between threads using shared variables, guarded by with mutexes/wait-conditions
- Stop the thread by setting a "quit" flag, which breaks the infinite while loop.
- The high-level, Qt way:
- Don't subclass QThread
- Use an event loop, which automatically lowers CPU usage when there are no events/signals to process (no infinite-while-loop, no sleep())
- Pass data between threads using signals + slots. No locking is required.
- Stop the thread by invoking QThread::quit(), which stops the event loop.
It sounds like you've combined both methods. Can you choose one instead? I think that will make your code work better.[/quote]
Thanks for your great help :-)
i searched for this and found a good example, here is the code:@#include <QtCore>
class Worker : public QObject
{
Q_OBJECT
private slots:
void onTimeout()
{
qDebug()<<"Worker::onTimeout get called from?: "<<QThread::currentThreadId();
}
};#include "main.moc"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug()<<"From main thread: "<<QThread::currentThreadId();QThread t; QTimer timer; Worker worker; QObject::connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout())); timer.start(1000); timer.moveToThread(&t); worker.moveToThread(&t); t.start(); return a.exec();
}
@
the link is "How to Use QThread in the Right Way":http://blog.debao.me/2013/08/how-to-use-qthread-in-the-right-way-part-1/i appreciate it if you confirm this.
but i still want you to explain why we don't need using locking when using signals and slots. -
You're welcome :)
I recommend reading http://qt-project.org/doc/qt-5/threads-synchronizing.html as a start.
Basically, if a variable/object is shared between threads, you need to make sure that it doesn't get modified by a thread while a 2nd thread is reading it. That's why you need locks.
With signals and slots, you don't need to share variables/objects. When you emit a signal that contains data, Qt will automatically make a copy for you behind the scenes. Then, it passes that copy to the 2nd thread. There is no sharing, so you don't have to worry about data corruption, so you don't need locks.
Note: Qt's container classes (e.g. QVector, QString, QList) are "implicitly shared":http://qt-project.org/doc/qt-5/implicit-sharing.html so copying them is very efficient. However, if you want to copy custom data structures, you may need to watch out for performance issues.
Are you confident with using signals and slots (in general) yet?
[quote]
@
#include "main.moc"
@
[/quote]You shouldn't include *.moc files directly. You should declare all your QObject-derived classes in .h files, and moc will process them automatically.[quote]
@
QObject::connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout()));
timer.start(1000);timer.moveToThread(&t); worker.moveToThread(&t); t.start();
@
[/quote]You don't need to move your timer.Other than that, it looks good :)
-
[quote author="JKSH" date="1387599076"]You're welcome :)
I recommend reading http://qt-project.org/doc/qt-5/threads-synchronizing.html as a start.
this was a very useful link thanks.
[quote]
@
#include "main.moc"
@
[/quote]You shouldn't include *.moc files directly. You should declare all your QObject-derived classes in .h files, and moc will process them automatically.yes you are right. actually i have copied this part from the link i provided earlier, and I'm not including "main.moc" in my project.
[quote]
@
QObject::connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout()));
timer.start(1000);timer.moveToThread(&t); worker.moveToThread(&t); t.start();
@
[/quote]You don't need to move your timer.Other than that, it looks good :)[/quote]
and by the way, by using Qtimer now cpu usage looks good. thanks for your great help.
-
Glad to hear, and you're welcome. Happy coding!