Related
My problem is the following one: I have 2 classes (mainwindow and mythread), I run the thread from the mainwindow and I would like to display some QLabel of my mainwindow from mythread :
mythread.cpp :
void mythread::run()
{
while(1)
{
this->read();
}
}
void mythread::read()
{
RF_Power_Control(&MonLecteur, TRUE, 0);
status = ISO14443_3_A_PollCard(&MonLecteur, atq, sak, uid, &uid_len);
if (status != 0){
//display Qlabel in mainwindow
}
}
mainwindow.cpp :
_thread = new mythread();
_thread->start();
You should use Qt's signal/slot mechanism. The thread will emit a signal, that new data has been read. Any interested object can connect to that signal, and perform actions depending on it.
This also works across thread-boundaries, as in your example. In Qt, it is required that only the main-thread interacts with UI elements.
Here is an outline:
// Your mainwindow:
class MyWindow : public QMainWindow {
Q_OBJECT
// as needed
private slots:
void setLabel(const QString &t) { m_label->setText(t); }
};
// Your thread
class MyThread: public QThread {
Q_OBJECT
// as needed
signals:
void statusUpdated(const QString &t);
};
// in your loop
if (status != 0) {
emit statusUpdated("New Status!");
}
// in your mainwindow
_thread = new MyThread;
connect(_thread, &MyThread::statusUpdated, this, &MyWindow::setLabel);
_thread->start();
I have a bigger project, with a GUI and I want to manage some files in the background. I've implemented a new thread for this task and on runtime, everything works great. But as soon as I quit the application visual-leak-detector finds 3-7 memory leaks.
I separated my threading code and created a new project to check this with a minimal code example, but I'm still not able to fix my issue.
I think it has something to do with the event loop of the main program. Maybe the loop doesn't process the last events to delete my thread class and the thread itself. Because I stop and quit the thread in a destructor. But I'm not sure on this one.
Here is my minimal code:
threadclass.hpp:
#include <QObject>
#include <QDebug>
class ThreadClass : public QObject {
Q_OBJECT
public:
explicit ThreadClass() {}
virtual ~ThreadClass(){
qDebug() << "ThreadClass Destructor";
}
signals:
// emit finished for event loop
void finished();
public slots:
// scan and index all files in lib folder
void scanAll(){
for(long i = 0; i < 10000; i++){
for (long k = 0; k < 1000000; k++);
if(i%500 == 0)
qDebug() << "thread: " << i;
}
}
// finish event loop and terminate
void stop(){
// will be processed after scanall is finished
qDebug() << "STOP SIGNAL --> EMIT FINSIHED";
emit finished();
}
};
threadhandler.hpp:
#include <QObject>
#include <QThread>
#include "threadclass.hpp"
class ThreadHandler : public QObject {
Q_OBJECT
public:
explicit ThreadHandler(QObject *parent = 0) : parent(parent), my_thread(Q_NULLPTR) {}
virtual ~ThreadHandler() {
// TODO Check!
// I think I don't have to delete the threads, because delete later
// on finish signal. Maybe I just have to wait, but then how do I
// check, if thread is not finished? Do I need to make a bool var again?
if (my_thread != Q_NULLPTR && my_thread->isRunning())
{
emit stopThread();
//my_thread->quit();
my_thread->wait();
//delete my_thread;
}
qDebug() << "ThreadHandler Destructor";
my_thread->dumpObjectInfo();
}
void startThread(){
if (my_thread == Q_NULLPTR)
{
my_thread = new QThread;
ThreadClass *my_threaded_class = new ThreadClass();
my_threaded_class->moveToThread(my_thread);
// start and finish
QObject::connect(my_thread, &QThread::started, my_threaded_class, &ThreadClass::scanAll);
QObject::connect(this, &ThreadHandler::stopThread, my_threaded_class, &ThreadClass::stop);
// finish cascade
// https://stackoverflow.com/a/21597042/6411540
QObject::connect(my_threaded_class, &ThreadClass::finished, my_threaded_class, &ThreadClass::deleteLater);
QObject::connect(my_threaded_class, &ThreadClass::destroyed, my_thread, &QThread::quit);
QObject::connect(my_thread, &QThread::finished, my_thread, &QThread::deleteLater);
my_thread->start();
}
}
signals:
void stopThread();
private:
QObject *parent;
QThread* my_thread;
};
The main.cpp is crappy but seems to simulate the behavior of my main program well enough:
#include <QCoreApplication>
#include <QTime>
#include <QDebug>
#include "threadhandler.hpp"
#include <vld.h>
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
ThreadHandler *th = new ThreadHandler();
th->startThread();
// wait (main gui programm)
QTime dieTime= QTime::currentTime().addSecs(5);
while (QTime::currentTime() < dieTime) {
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
qDebug() << "DELETE TH";
delete th;
qDebug() << "FINISH ALL EVENTS";
QCoreApplication::processEvents(QEventLoop::AllEvents, 500);
qDebug() << "QUIT";
QCoreApplication::quit();
qDebug() << "CLOSE APP";
// pause console
getchar();
// return a.exec();
}
Here is the output from VLD:
WARNING: Visual Leak Detector detected memory leaks!
...
turns out this is very boring and uninteresting
...
Visual Leak Detector detected 3 memory leaks (228 bytes).
Largest number used: 608 bytes.
Total allocations: 608 bytes.
Visual Leak Detector is now exiting.
The program '[8708] SOMinimalExampleThreads.exe' has exited with code 0 (0x0).
Update 1: I added qDebug() << "ThreadClass Destructor"; to the destructor of the ThreadClass and my new output looks like this:
...
thread: 9996
thread: 9997
thread: 9998
thread: 9999
ThreadHandler Destructor
FINISH ALL EVENTS
CLOSE APP
Now it is clear that the destructor of my threaded class is never called and therefore lost in the void. But then why is this not working?
QObject::connect(my_threaded_class, &ThreadClass::finished, my_threaded_class, &ThreadClass::deleteLater);
Update 2: I found one problem in ThreadHandler:
emit stopThread();
my_thread->quit(); // <-- stops the event loop and therefore no deletelater
my_thread->wait();
I removed my_thread->quit() and now the destructor of ThreadClass is called, but my_thread->wait() never finishes.
Problem description:
When the destructor of ThreadHandler emits stopThread from the main thread, Qt invokes the connected slot (&ThreadClass::stop) by posting an event into the worker thread's event loop (aka, a queued connection). This means that the event loop of the worker's needs to be ready for receiving new events when this signal is emitted (since you are relying on it to perform proper clean-up). However, as you've already spotted, the call to thread->quit() might cause the event loop to quit earlier than desired (before the worker thread makes its way to call ThreadClass::stop, and hence the signal ThreadClass::finished is not emitted). You might want to examine the output of this minimal example that reproduces the behavior I am talking about:
#include <QtCore>
/// lives in a background thread, has a slot that receives an integer on which
/// some work needs to be done
struct WorkerObject : QObject {
Q_OBJECT
public:
using QObject::QObject;
Q_SLOT void doWork(int x) {
// heavy work in background thread
QThread::msleep(100);
qDebug() << "working with " << x << " ...";
}
};
/// lives in the main thread, has a signal that should be connected to the
/// worker's doWork slot; to offload some work to the background thread
struct ControllerObject : QObject {
Q_OBJECT
public:
using QObject::QObject;
Q_SIGNAL void sendWork(int x);
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QThread thread;
WorkerObject workerObj;
workerObj.moveToThread(&thread);
// quit application when worker thread finishes
QObject::connect(&thread, &QThread::finished, &a, &QCoreApplication::quit);
thread.start();
ControllerObject controllerObj;
QObject::connect(&controllerObj, &ControllerObject::sendWork, &workerObj,
&WorkerObject::doWork);
for (int i = 0; i < 100; i++) {
QThread::msleep(1);
// call thread.quit() when i is equal to 10
if (i == 10) {
thread.quit();
}
controllerObj.sendWork(i);
}
return a.exec();
}
#include "main.moc"
On my machine, Here is a possible output:
working with 0 ...
working with 1 ...
working with 2 ...
working with 3 ...
Notice that, despite the fact that thread.quit() is called on the tenth iteration from the main thread, the worker thread might not process all messages received before the exit call (and we get the value 3 as the last value processed by the worker).*
Solution:
Actually, Qt provides a canonical to way to quit a worker thread and perform necessary clean-up, since the signal QThread::finished is handled in a special way:
When this signal is emitted, the event loop has already stopped running. No more events will be processed in the thread, except for deferred deletion events. This signal can be connected to QObject::deleteLater(), to free objects in that thread.
This means that you can use thread->quit() (the same way you were doing), but you just need to add:
connect(my_thread, &QThread::finished, my_threaded_class, &ThreadClass::stop);
to your startThread and remove the unnecessary emit stopThread(); from the destructor.
* I couldn't find any page in the documentation that explains this behavior in detail, so I provided this minimal example to explain what I am talking about.
I found out that the my_thread->wait() function, blocks the event loop and therefore the quit and deleteLater cascade is never finished. I fixed this with another method of waiting for the finished thread:
removed
Here is the newly implemented solution from Mike. It was pretty easy to implement, I only had to change the connection to my_threaded_class::stop in the threadhandler class.
#include <QObject>
#include <QThread>
#include "threadclass.hpp"
class ThreadHandler : public QObject {
Q_OBJECT
public:
explicit ThreadHandler(QObject *parent = 0) : parent(parent), my_thread(Q_NULLPTR) {}
virtual ~ThreadHandler() {
if (my_thread != Q_NULLPTR && my_thread->isRunning())
{
my_thread->quit();
my_thread->wait();
}
qDebug() << "ThreadHandler Destructor";
}
void startThread(){
if (my_thread == Q_NULLPTR)
{
my_thread = new QThread;
ThreadClass *my_threaded_class = new ThreadClass();
my_threaded_class->moveToThread(my_thread);
// start and finish
QObject::connect(my_thread, &QThread::started, my_threaded_class, &ThreadClass::scanAll);
// https://stackoverflow.com/questions/53468408
QObject::connect(my_thread, &QThread::finished, my_threaded_class, &ThreadClass::stop);
// finish cascade
// https://stackoverflow.com/a/21597042/6411540
QObject::connect(my_threaded_class, &ThreadClass::finished, my_threaded_class, &ThreadClass::deleteLater);
QObject::connect(my_threaded_class, &ThreadClass::destroyed, my_thread, &QThread::quit);
QObject::connect(my_thread, &QThread::finished, my_thread, &QThread::deleteLater);
my_thread->start();
}
}
signals:
void stopThread();
private:
QObject *parent;
QThread* my_thread;
};
I'm trying to get large data from a database but when running the main window freezes.
I am working under windows and according to this link Windows automatically set the program to a hanging state state after 5 seconds.
Is there a way to prevent freezing?
Here is the code:
void MainWindow::on_getDataButtonClicked()
{
ui->centralWidget->setEnabled(false);
QApplication::setOverrideCursor(Qt::WaitCursor);
try
{
Client client(user, password);
std::future<map<string, map<string, string> > > fut =
std::async(std::launch::async, &Client::get_data, &client);
// While not all data has been retrieved, set message to the status bar.
while (fut.wait_for(std::chrono::seconds(0)) != std::future_status::ready)
{
ui->statusBar->showMessage("Getting data.");
std::this_thread::sleep_for(std::chrono::milliseconds(500));
ui->statusBar->showMessage("Getting data..");
std::this_thread::sleep_for(std::chrono::milliseconds(500));
ui->statusBar->showMessage("Getting data...");
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
map<string, map<string, string> > exported_strings = std::move(fut.get());
ui->statusBar->showMessage("%All data has been retrieved!");
}
catch (std::string& s)
{
QMessageBox::critical(this, "Error", QString::fromStdString(s));
}
catch (std::exception& e)
{
QMessageBox::critical(this, "Error", QString(e.what()));
}
catch (...)
{
QMessageBox::critical(this, "Error", "An unknown error has occurred.");
}
ui->centralWidget->setEnabled(true);
QApplication::restoreOverrideCursor();
}
On a side note, the main window does not freezes when debugging.
There's no point to doing asynchronous work if you're waiting for it and blocking the GUI thread anyway in the while loop. You need to get rid of the while loop.
You could use QtConcurrent::run instead of std::async, and use QFutureWatcher to get notified asynchronously, without blocking, when the async task has finished.
// https://github.com/KubaO/stackoverflown/tree/master/questions/async-sane-39396761
#include <QtWidgets>
#include <QtConcurrent>
#include <map>
#include <string>
struct Client {
using result_type = std::map<std::string, std::map<std::string, std::string>>;
result_type get_data() {
QThread::sleep(5); // pretend to do some work
return result_type();
}
};
class MainWindow : public QMainWindow {
Q_OBJECT
Client::result_type exported_strings;
QWidget centralWidget;
QVBoxLayout layout{¢ralWidget};
QPushButton getDataButton{"Get Data"};
QStatusBar statusBar;
QTimer statusTimer;
QString statusMessage;
void setBusyStatus(const QString & status) {
centralWidget.setEnabled(false);
QApplication::setOverrideCursor(Qt::WaitCursor);
statusMessage = status;
statusTimer.start(0);
}
void setNormalStatus(const QString & status) {
centralWidget.setEnabled(true);
QApplication::restoreOverrideCursor();
statusBar.showMessage(status);
statusTimer.stop();
}
Q_SLOT void on_getDataButtonClicked();
public:
MainWindow() {
setStatusBar(&statusBar);
setCentralWidget(¢ralWidget);
layout.addWidget(&getDataButton);
int n = 0;
connect(&statusTimer, &QTimer::timeout, [=]() mutable {
statusBar.showMessage(QStringLiteral("%1%2").arg(statusMessage).arg(QString{n+1, QChar{'.'}}));
n = (n+1)%3;
statusTimer.start(500);
});
connect(&getDataButton, &QPushButton::clicked, this, &MainWindow::on_getDataButtonClicked);
}
};
void MainWindow::on_getDataButtonClicked()
{
auto future = QtConcurrent::run([=]{
Client client;
return client.get_data();
});
auto watcher = new QFutureWatcher<Client::result_type>{this};
connect(watcher, &QFutureWatcher<Client::result_type>::finished, this, [=]{
exported_strings = std::move(watcher->result());
watcher->deleteLater();
setNormalStatus("All data has been retrieved!");
});
watcher->setFuture(future);
setBusyStatus("Getting data");
}
int main(int argc, char ** argv) {
QApplication app{argc, argv};
MainWindow w;
w.show();
return app.exec();
}
#include "main.moc"
Alternatively, you could emit a signal from the async code, letting you retain the use of std::async if you prefer that:
#include <QtWidgets>
#include <future>
#include <map>
#include <string>
struct Client {
using result_type = std::map<std::string, std::map<std::string, std::string>>;
result_type get_data() {
QThread::sleep(5); // pretend to do some work
return result_type();
}
};
class MainWindow : public QMainWindow {
Q_OBJECT
Client::result_type exported_strings;
QWidget centralWidget;
QVBoxLayout layout{¢ralWidget};
QPushButton getDataButton{"Get Data"};
QStatusBar statusBar;
QTimer statusTimer;
QString statusMessage;
std::future<Client::result_type> resultFuture;
void setBusyStatus(const QString & status) {
centralWidget.setEnabled(false);
QApplication::setOverrideCursor(Qt::WaitCursor);
statusMessage = status;
statusTimer.start(0);
}
void setNormalStatus(const QString & status) {
centralWidget.setEnabled(true);
QApplication::restoreOverrideCursor();
statusBar.showMessage(status);
statusTimer.stop();
}
Q_SLOT void on_getDataButtonClicked();
Q_SIGNAL void hasResult();
public:
MainWindow() {
setStatusBar(&statusBar);
setCentralWidget(¢ralWidget);
layout.addWidget(&getDataButton);
int n = 0;
connect(&statusTimer, &QTimer::timeout, [=]() mutable {
statusBar.showMessage(QStringLiteral("%1%2").arg(statusMessage).arg(QString{n+1, QChar{'.'}}));
n = (n+1)%3;
statusTimer.start(500);
});
connect(&getDataButton, &QPushButton::clicked, this, &MainWindow::on_getDataButtonClicked);
}
};
void MainWindow::on_getDataButtonClicked()
{
connect(this, &MainWindow::hasResult, this, [this](){
exported_strings = std::move(resultFuture.get());
setNormalStatus("All data has been retrieved!");
}, Qt::UniqueConnection);
resultFuture = std::async(std::launch::async, [this]{
Client client;
auto result = client.get_data();
emit hasResult();
return result;
});
setBusyStatus("Getting data");
}
int main(int argc, char ** argv) {
QApplication app{argc, argv};
MainWindow w;
w.show();
return app.exec();
}
#include "main.moc"
If everything is happening on the same thread and some operation is slow, then you won't get back to the event-loop in time for the OS to not declare you as stuck.
The solution(s) are to either;
1) use multiple processes.
2) use multiple threads.
3) divide the work on your one thread into chunks that are all guaranteed to be small enough that you can get back to servicing the eventloop in a timely fashion.
To avoid multithreading, here's what you should do:
Cut your code (virtually, just in thought) into chunks
Run your program and study these chunks, and see which of them causes the freezing (takes longest to execute)
Once you know what part causes the freezing, use QApplication::processEvents(); to force the MainWindow to become responsive.
Example: Let me illustrate with an example. Say you have this function
void MainWindow::on_getDataButtonClicked()
{
//do some easy stuff
for(long i = 0; i < 100000; i++)
{
//do some stuff
//do some other stuff
}
//do some different stuff
}
Now while executing this nasty function, your window will definitely freeze until the whole function is finished. The "easy stuff" is not the problem, because it's fast. But the big loop is the problem. So all you have to do is tell Qt to reprocess the events that get the main window to become responsive again; Like this:
void MainWindow::on_getDataButtonClicked()
{
//do some easy stuff
for(long i = 0; i < 100000; i++)
{
QApplication::processEvents();
//do some stuff
//do some other stuff
}
QApplication::processEvents();
//do some different stuff
}
How many times should you call processEvents();? Call it whenever you want the window to respond again. If in doubt, just put processEvents() like everywhere! Infest every line with that. It's not the best practice, but then you can remove them one by one and see where your program starts freezing again.
One additional piece of advice: Don't throw std::string exceptions. This is a very bad idea. Learn how to throw classes inherited from std::exception. You can still hold your string inside the std::exception object. For example, you can do: throw std::exception("This is very very bad");. There are many other classes you can throw. Find them here.
QT 5.1.0rc2 , msvc 2010 , Microsoft Visual Studio 2010
It is working code on Qt 4.8.4 , msvc 2008
I have compile error at
#if defined( HANDLE_PROCESSING_IN_MAIN_THREAD )
if(QThread::currentThread() != this)
emit started();
#endif
inherited::run();
and
#if defined( HANDLE_PROCESSING_IN_MAIN_THREAD )
if(QThread::currentThread() != this)
emit finished();
#endif
error C2660: 'QThread::started' : function does not take 0 arguments
error C2660: 'QThread::finished' : function does not take 0 arguments
In QThread i have seen
Q_SIGNALS:
void started(
#if !defined(Q_QDOC)
QPrivateSignal
#endif
);
void finished(
#if !defined(Q_QDOC)
QPrivateSignal
#endif
);
when I defined Q_QDOC I got many errors in QT sources.
QPrivateSignal is empty structure that defined in macro Q_OBJECT
Need a solution that does not affect the architecture of the application, as to be backward compatible with Qt4.8.4
Some ideas?
The thread's signals are emitted automatically. You should never emit them manually.
You're trying to use preprocessor to handle two variants of the code: processing in the gui thread or a dedicated thread. Qt provides a very easy way of dealing with it.
Implement your processing functionality in a slot in a class deriving from QObject. You could also do it in the reimplemented event() method if it's easier for you to start processing by posting an event rather than invoking a slot.
If you want your object's slots to run in a different thread, use the moveToThread() method.
You don't need to derive from QThread. Its default implementation of run() spins a local event loop.
If you want your object to be compatible with living in the GUI thread, it must do its processing in small chunks and relinquish control so that the GUI doesn't stall.
Below is a complete example that demonstrates how you can start the worker object in either GUI or a separate thread, and how you can safely move it between threads.
// https://github.com/KubaO/stackoverflown/tree/master/questions/qobject-thread-18653347
#include <QtGui>
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
#include <QtWidgets>
#endif
/// See http://stackoverflow.com/a/40382821/1329652
bool isSafe(QObject * obj) {
Q_ASSERT(obj->thread() || qApp && qApp->thread() == QThread::currentThread());
auto thread = obj->thread() ? obj->thread() : qApp->thread();
return thread == QThread::currentThread();
}
class Helper : private QThread {
public:
using QThread::usleep;
};
class Worker : public QObject {
Q_OBJECT
int m_counter;
QBasicTimer m_timer;
void timerEvent(QTimerEvent * ev) override;
public:
Worker(QObject *parent = nullptr) : QObject(parent) {}
/// This method is thread-safe.
Q_SLOT void start() {
if (!isSafe(this)) return (void)QMetaObject::invokeMethod(this, "start");
if (m_timer.isActive()) return;
m_counter = 0;
m_timer.start(0, this);
}
/// This method is thread-safe.
Q_INVOKABLE void startInThread(QObject *targetThread) {
if (!isSafe(this)) return (void)QMetaObject::invokeMethod(this, "startInThread", Q_ARG(QObject*, targetThread));
QObject::moveToThread(qobject_cast<QThread*>(targetThread));
start();
}
Q_SIGNAL void done();
Q_SIGNAL void progress(int percent, bool inGuiThread);
};
void Worker::timerEvent(QTimerEvent * ev)
{
const int busyTime = 50; // [ms] - longest amount of time to stay busy
const int testFactor = 128; // number of iterations between time tests
const int maxCounter = 30000;
if (ev->timerId() != m_timer.timerId()) return;
const auto inGuiThread = []{ return QThread::currentThread() == qApp->thread(); };
QElapsedTimer t;
t.start();
while (1) {
// do some "work"
Helper::usleep(100);
m_counter ++;
// exit when the work is done
if (m_counter > maxCounter) {
emit progress(100, inGuiThread());
emit done();
m_timer.stop();
break;
}
// exit when we're done with a timed "chunk" of work
// Note: QElapsedTimer::elapsed() may be expensive, so we call it once every testFactor iterations
if ((m_counter % testFactor) == 0 && t.elapsed() > busyTime) {
emit progress(m_counter*100/maxCounter, inGuiThread());
break;
}
}
}
class Window : public QWidget {
Q_OBJECT
QVBoxLayout m_layout{this};
QPushButton m_startGUI{"Start in GUI Thread"};
QPushButton m_startWorker{"Start in Worker Thread"};
QLabel m_label;
QThread m_thread{this};
Worker m_worker;
Q_SLOT void showProgress(int p, bool inGuiThread) {
m_label.setText(QString("%1 % in %2 thread")
.arg(p).arg(inGuiThread ? "gui" : "worker"));
}
Q_SLOT void on_startGUI_clicked() {
m_worker.startInThread(qApp->thread());
}
Q_SLOT void on_startWorker_clicked() {
m_worker.startInThread(&m_thread);
}
public:
Window(QWidget *parent = {}, Qt::WindowFlags f = {}) : QWidget(parent, f) {
m_layout.addWidget(&m_startGUI);
m_layout.addWidget(&m_startWorker);
m_layout.addWidget(&m_label);
m_thread.start();
connect(&m_worker, SIGNAL(progress(int,bool)), SLOT(showProgress(int,bool)));
connect(&m_startGUI, SIGNAL(clicked(bool)), SLOT(on_startGUI_clicked()));
connect(&m_startWorker, SIGNAL(clicked(bool)), SLOT(on_startWorker_clicked()));
}
~Window() {
m_thread.quit();
m_thread.wait();
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Window w;
w.show();
return a.exec();
}
#include "main.moc"
I made this server class that starts a thread when new connection comes in. It works ok with some cases, but it's not very stable. I am trying to solve where it breaks. My debugger tells me something about qmutex. If anyone can spot the problem. ty
It connects with parent with signal&slots and gets data back also.
Here is the header:
#ifndef FORTUNESERVER_H
#define FORTUNESERVER_H
#include <QStringList>
#include <QTcpServer>
#include <QThread>
#include <QTcpSocket>
#include <string>
using namespace std;
class FortuneServer : public QTcpServer
{
Q_OBJECT
public:
FortuneServer(QObject *parent = 0);
public slots:
void procesServerString(string serverString);
void getStringToThread(string serverString);
protected:
void incomingConnection(int socketDescriptor);
private:
QStringList fortunes;
signals:
void procesServerStringToParent(string serverString);
void getStringToThreadSignal(string serverString);
};
class FortuneThread : public QObject
{
Q_OBJECT
public:
FortuneThread(int socketDescriptor, QObject *parent);
public slots:
void getString();
void sendString(string sendoutString);
signals:
void error(QTcpSocket::SocketError socketError);
void fromThreadString(string serverString);
void finished();
private:
int socketDescriptor;
QString text;
QTcpSocket tcpSocket;
};
#endif
and cc:
#include <stdlib.h>
#include <QtNetwork>
#include "MeshServer.hh"
#include <iostream>
#include "TableView.hh"
using namespace std;
FortuneServer::FortuneServer(QObject *parent)
: QTcpServer(parent)
{
}
void FortuneServer::procesServerString(string serverString){
emit procesServerStringToParent(serverString);
}
void FortuneServer::getStringToThread(string serverString){
emit getStringToThreadSignal(serverString);
}
void FortuneServer::incomingConnection(int socketDescriptor)
{
FortuneThread *serverthread = new FortuneThread(socketDescriptor, this);
//connect(&serverthread, SIGNAL(finished()), &serverthread, SLOT(deleteLater()));
QThread* thread = new QThread;
serverthread->moveToThread(thread);
connect(thread, SIGNAL(started()), serverthread, SLOT(getString()));
connect(serverthread, SIGNAL(fromThreadString(string)), this, SLOT(procesServerString(string)));
connect(this, SIGNAL(getStringToThreadSignal(string)), serverthread, SLOT(sendString(string)));
connect(serverthread, SIGNAL(finished()), thread, SLOT(quit()));
connect(serverthread, SIGNAL(finished()), serverthread, SLOT(deleteLater()));
connect(serverthread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
FortuneThread::FortuneThread(int socketDescriptor, QObject *parent)
: QObject(parent), socketDescriptor(socketDescriptor)
{
}
void FortuneThread::getString()
{
if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
emit error(tcpSocket.error());
cout<<"socket error"<<endl;
return;
}
//in part
if(!tcpSocket.waitForReadyRead(10000)){
emit finished();
return;
}
int joj = tcpSocket.bytesAvailable();
char inbuffer[1024];
tcpSocket.read(inbuffer,1024);
string instring;
instring = inbuffer;
instring.resize(joj);
emit fromThreadString(instring);
}
void FortuneThread::sendString(string sendoutString)
{
//out part
char buffer[1024];
int buffer_len = 1024;
int bytecount;
memset(buffer, '\0', buffer_len);
string outstring = sendoutString;
int TempNumOne= (int)outstring.size();
for (int a=0;a<TempNumOne;a++)
{
buffer[a]=outstring[a];
}
QByteArray block;
block = buffer;
tcpSocket.write(block);
tcpSocket.disconnectFromHost();
tcpSocket.waitForDisconnected();
emit finished();
}
this is from parent:
//server start
QHostAddress adr;
adr.setAddress( QString("127.0.0.1") );
adr.toIPv4Address();
quint16 port = 1101;
if (!server.listen( adr, port)) {
QMessageBox::critical(this, tr("CR_bypasser"),
tr("Unable to start the server: %1.")
.arg(server.errorString()));
close();
return;
}
QString ipAddress;
ipAddress = server.serverAddress().toString();
statusLabel->setText(tr("The server is running on\n\nIP: %1\nport: %2\n\n"
"Run the Fortune Client example now.")
.arg(ipAddress).arg(server.serverPort()));
connect (&server, SIGNAL(procesServerStringToParent(string)), this, SLOT(procesServerString(string)));
connect (this, SIGNAL(StringToServer(string)), &server, SLOT(getStringToThread(string)));
edit: what I am trying to do:
I have a client (part of a game engine(Cryengine)) that I made to send string of ingame coordinates and some other things with a socket like its done in a link I gave before. This works ok. I get data on "127.0.0.1" port 1101. Now I just need this data to be evaluated in my own program, that has this TableView class, inside which I can collect coordinates I get from the string, callculate some data from coordinates and then return this new string back through the server to gameengine. In game I will click on objects get their coor., make a string out of that (containing coor,entityid, etc..), send this string to server, that returns callculated info from TableView. I just need this one way flow only one client that is sending strings. I am not sure about recv(hsock, buffer, buffer_len, 0), I guess node that is responsible for string sending in game will wait for return string? This is one of my first programs atm I am realy confused...
The code you present is exemplary of cargo cult coding: you do various unnecessary things, apparently in hopes of fixing the problem.
The Likely Crasher ...
There are tons of problems with the code, but I think the cause of the crash is this: tcpSocket.write(block) does not send out a zero-terminated string down the wire. The block is zero-terminated, but the assignment to a byte array does not add this zero termination to the size() of QByteArray. The following code prints 1, even though there is a zero terminating byte internally in the contents of the byte array.
QByteArray arr = "a";
qDebug("len=%d", arr.size());
The receiving code expects the zero termination, but never receives it. You then proceed to assign a non-zero-terminated buffer to std::string:
string instring;
instring = inbuffer;
instring.resize(joj);
The subsequent resize is cargo cult: you're trying to fix the problem after std::string & std::string::operator=(const char*) has already read past your buffer, in all likelihood.
Do not take this to mean that fixing just that is the right way to proceed. Not at all. The right way to proceed is to delete the code you wrote and do it right, without a ton of unnecessary incantations that don't help.
... and All The Other Problems
You've fallen into the trap of believing in magic, perpetuated endlessly in various forums.
The threads are not magical objects that you can just apply to any problem out there in hopes that they help. I don't know what makes people think that threads are magical, but the rule of thumb is: If someone tells you "oh, you should try threads", they are most likely wrong. If they tell that in relation to networking, they are pretty much never right, they are unhelpful, and they don't understand your problem at all (neither do you, it seems). More often than not, threads will not help unless you clearly understand your problem. Qt's networking system is asynchronous: it doesn't block the execution of your code, if you don't use the waitxxxx() functions. You shouldn't use them, by the way, so all is good here. No need for a bazillion threads.
So, it is completely unnecessary to start a new thread per each incoming connection. It will decrease the performance of your server -- especially if the server does simple processing because you add the overhead of context switching and thread creation/dismantling to each connection. You want less than 2 threads per each core in your system, so using QThread::idealThreadCount() for the number of threads in the pool would be a good starting point.
You are also depriving yourself of the benefit of threading since you use the networking thread only to receive the data, and you then send out a fromThreadString(string) signal. I presume that signal is sent to your application's main thread. Now that's just silly, because receiving a bunch of bytes from a network socket is downright trivial. Your threads don't do any work, all the work they do is wasted on their creation and removal.
The code below is a simple example of how one might correctly use the Qt APIs to implement a client-server system that distributes work across the physical cores in a round-robin fashion. It should perform quite well. The Fortune client example included in Qt is very unfortunate indeed, because it's precisely the wrong way to go about things.
What one will notice is:
It's not entirely trivial. Qt could be more helpful, but isn't.
Both the clients and the senders are moved into threads from a thread pool.
Disconnected clients are not deleted, but merely returned to a list of clients
kept by the tread pool. They are reused when a client is called for.
QThread is not derived from. QTcpServer is only derived to access the socket handle.
No functions whose name begins with wait() are used. Everything is handled asynchronously.
The ThreadPool keeps a looked-up QMetaMethod for the newConnection(int) slot of the Client. This is faster than using QMetaObject::invokeMethod() as it has to look things up every time.
A timer running in the main thread sets off a signal-slot chain by deleting the first sender. Each senders' deletion triggers the deletion of the next one. Eventually, the last sender sets off the quit() slot in the thread pool. The latter emits the finished() signal when all threads are indeed finished.
#include <QtCore/QCoreApplication>
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtCore/QQueue>
#include <QtCore/QThread>
#include <QtCore/QTimer>
#include <QtCore/QMetaMethod>
// Processes data on a socket connection
class Client : public QObject
{
Q_OBJECT
public:
Client(QObject* parent = 0) : QObject(parent), socket(new QTcpSocket(this))
{
connect(socket, SIGNAL(readyRead()), SLOT(newData()));
connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
SLOT(newState(QAbstractSocket::SocketState)));
qDebug("Client()");
}
~Client() { qDebug("~Client()"); }
signals:
void done();
public slots:
void newConnection(int descriptor) {
socket->setSocketDescriptor(descriptor);
}
private slots:
void newData() {
QByteArray data = socket->readAll();
if (0) qDebug("got %d bytes", data.size());
if (0) qDebug("got a string %s", data.constData());
// here we can process the data
}
void newState(QAbstractSocket::SocketState state) {
qDebug("client new state %d", state);
if (state == QAbstractSocket::UnconnectedState) { emit done(); }
}
protected:
QTcpSocket* socket;
int descriptor;
};
// Connects to a client and sends data to it
class Sender : public QObject
{
Q_OBJECT
public:
Sender(const QString & address, quint16 port, QObject * parent = 0) :
QObject(parent), socket(new QTcpSocket(this)),
bytesInFlight(0), maxBytesInFlight(65536*8)
{
connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
SLOT(newState(QAbstractSocket::SocketState)));
connect(socket, SIGNAL(bytesWritten(qint64)), SLOT(sentData(qint64)));
socket->connectToHost(address, port);
qDebug("Sender()");
}
~Sender() { qDebug("~Sender()"); }
protected:
// sends enough data to keep a maximum number of bytes in flight
void sendData() {
qint64 n = maxBytesInFlight - bytesInFlight;
if (n <= 0) return;
bytesInFlight += n;
socket->write(QByteArray(n, 44)); // 44 is the answer, after all
}
protected slots:
void sentData(qint64 n) {
bytesInFlight -= n;
Q_ASSERT(bytesInFlight >= 0);
sendData();
}
void newState(QAbstractSocket::SocketState state) {
qDebug("sender new state %d", state);
if (state == QAbstractSocket::ConnectedState) sendData();
}
protected:
QTcpSocket* socket;
qint64 bytesInFlight;
qint64 maxBytesInFlight;
};
// Keeps track of threads and client objects
class ThreadPool : public QTcpServer
{
Q_OBJECT
public:
ThreadPool(QObject* parent = 0) : QTcpServer(parent), nextThread(0) {
for (int i=0; i < QThread::idealThreadCount(); ++i) {
QThread * thread = new QThread(this);
connect(thread, SIGNAL(finished()), SLOT(threadDone()));
thread->start();
threads << thread;
}
const QMetaObject & mo = Client::staticMetaObject;
int idx = mo.indexOfMethod("newConnection(int)");
Q_ASSERT(idx>=0);
method = mo.method(idx);
}
void poolObject(QObject* obj) const {
if (nextThread >= threads.count()) nextThread = 0;
QThread* thread = threads.at(nextThread);
obj->moveToThread(thread);
}
protected:
void incomingConnection(int descriptor) {
Client * client;
if (threads.isEmpty()) return;
if (! clients.isEmpty()) {
client = clients.dequeue();
} else {
client = new Client();
connect(client, SIGNAL(done()), SLOT(clientDone()));
}
poolObject(client);
method.invoke(client, Q_ARG(int, descriptor));
}
signals:
void finished();
public slots:
void quit() {
foreach (QThread * thread, threads) { thread->quit(); }
}
private slots:
void clientDone() {
clients.removeAll(qobject_cast<Client*>(sender()));
}
void threadDone() {
QThread * thread = qobject_cast<QThread*>(sender());
if (threads.removeAll(thread)) delete thread;
if (threads.isEmpty()) emit finished();
}
private:
QList<QThread*> threads;
QQueue<Client*> clients;
QMetaMethod method;
mutable int nextThread;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
ThreadPool server;
if (!server.listen(QHostAddress::Any, 1101)) qCritical("cannot establish a listening server");
const int senderCount = 10;
Sender *prevSender = 0, *firstSender = 0;
for (int i = 0; i < senderCount; ++ i) {
Sender * sender = new Sender("localhost", server.serverPort());
server.poolObject(sender);
if (!firstSender) firstSender = sender;
if (prevSender) sender->connect(prevSender, SIGNAL(destroyed()), SLOT(deleteLater()));
prevSender = sender;
}
QTimer::singleShot(3000, firstSender, SLOT(deleteLater())); // run for 3s
server.connect(prevSender, SIGNAL(destroyed()), SLOT(quit()));
qApp->connect(&server, SIGNAL(finished()), SLOT(quit()));
// Deletion chain: timeout deletes first sender, then subsequent senders are deleted,
// finally the last sender tells the thread pool to quit. Finally, the thread pool
// quits the application.
return a.exec();
}
#include "main.moc"
Given your explanation, you game engine starts up and creates a connection to some port on localhost. Your Qt program is supposed to accept that connection on port 1101, receive some strings, process them, then send them back.
The code is modified to accept the connection on a fixed port number. All of the data processing, including sending the response back, has to be done from the newData() slot. You can also pass that data off to a different thread, if your computations are very complex. By complex I mean tens of thousands of operations like additions and multiplications, or thousands of trig operations.
The Sender class is there just as an example. Your game engine does the sending, of course, so you don't need the Sender class.
I got my old "the wrong way to do it" code to work. I guess this part was where the error was:
//removed
tcpSocket.disconnectFromHost();
tcpSocket.waitForDisconnected();
emit finished();
...
#include <stdlib.h>
#include <QtNetwork>
#include "MeshServer.hh"
#include <iostream>
#include "TableView.hh"
using namespace std;
FortuneServer::FortuneServer(QObject *parent)
: QTcpServer(parent)
{
}
void FortuneServer::procesServerString(string serverString){
emit procesServerStringToParent(serverString);
}
void FortuneServer::getStringToThread(string serverString){
emit getStringToThreadSignal(serverString);
}
void FortuneServer::incomingConnection(int socketDescriptor)
{
FortuneThread *serverthread = new FortuneThread(socketDescriptor, this);
//connect(&serverthread, SIGNAL(finished()), &serverthread, SLOT(deleteLater()));
QThread* thread = new QThread;
serverthread->moveToThread(thread);
connect(serverthread, SIGNAL(fromThreadString(string)), this, SLOT(procesServerString(string)));
connect(this, SIGNAL(getStringToThreadSignal(string)), serverthread, SLOT(sendString(string)));
connect(serverthread, SIGNAL(finished()), thread, SLOT(quit()));
connect(serverthread, SIGNAL(finished()), serverthread, SLOT(deleteLater()));
connect(serverthread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
}
FortuneThread::FortuneThread(int socketDescriptor, QObject *parent): QObject(parent), socketDescriptor(socketDescriptor)
{
if (!tcpSocket.setSocketDescriptor(socketDescriptor)) {
emit error(tcpSocket.error());
cout<<"socket error"<<endl;
emit finished();
return;
}
connect(&tcpSocket, SIGNAL(readyRead()), this, SLOT(getString()));
//connect(&tcpSocket, SIGNAL(disconnected()), this, SLOT(ondisconnected()));
}
void FortuneThread::getString()
{
int joj = tcpSocket.bytesAvailable();
if(joj==0){
tcpSocket.disconnectFromHost();
emit finished();
return;
}
char inbuffer[1024];
int buffer_len = 1024;
memset(inbuffer, '\0', buffer_len);
tcpSocket.read(inbuffer,1024);
string instring;
instring = inbuffer;
instring.resize(joj);
emit fromThreadString(instring);
}
void FortuneThread::sendString(string sendoutString)
{
char buffer2[1024];
int buffer_len = 1024;
memset(buffer2, '\0', buffer_len);
strcat(buffer2,sendoutString.c_str());
tcpSocket.write(buffer2,buffer_len);
}
void FortuneThread::ondisconnected()
{
emit finished();
}