I'm trying to correct a large program for memory leaks and threads that are not stopped. I know I have some, but I'm not sure about how to properly identify and kill them, so I started playing with some canonical examples, and I'm already having those.

First I tried the simplest thing:

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    return a.exec();
}

That gives me one (1) running thread in the Task Manager.

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    WorkerOne *w = new WorkerOne();
    QTimer::singleShot(3456, w, SLOT(stop()));
    return a.exec();
}

This one gives me 1 before starting the worker, then 2 until the thread actually starts (process is called), then 3 until the singleShot signal is captured and the worker deleted and then 2 again. So I'm having something loose there.

And this is the code for WorkerOne:

WorkerOne::WorkerOne(QObject *parent)
    : QObject(parent)
    , m_stop(false) {
    QThread* thread = new QThread;
    this->moveToThread(thread);
    connect(this, SIGNAL(error(QString)), this, SLOT(errorString(QString)));
    connect(thread, SIGNAL(started()), this, SLOT(process()));
    connect(this, SIGNAL(finished()), thread, SLOT(quit()));
    connect(this, SIGNAL(finished()), this, SLOT(deleteLater()));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    thread->start();
}

WorkerOne::~WorkerOne() {}

void WorkerOne::process() {
    while(!m_stop) {
        QEventLoop loop; QTimer::singleShot(1000, &loop, SLOT(quit())); loop.exec();
    }
    emit finished();
}

void WorkerOne::stop() {
    m_stop = true;
}

void WorkerOne::errorString(QString err) { }

The platform is Qt 5.2.1 with mingw48_32 compiler.

I think I am following the steps in threading howto from Maya Posch's blog, but maybe I am missing something.

有帮助吗?

解决方案

Your implementation of the worker object is literally upside down. It's QThread's job to spin the event loop. Your worker object should simply be driven by slot calls and incoming events. A processing busy loop idiom uses a zero-length timer to stay active while allowing the event loop to receive events and quit, with no need for extra flags.

Here's how to do it:

class WorkerOne : public QObject {
  Q_OBJECT
  QBasicTimer m_timer;
  void processChunk() {
    ...
  }
  void timerEvent(QTimerEvent * ev) {
    if (ev->timerId() == m_timer.timerId()) processChunk();
  }
public:
  WorkerOne(QObject * parent = 0) : QObject(parent) {}
  Q_SLOT void start() { m_timer.start(0, this); }
};

int main(int argc, char *argv[]) {
  QCoreApplication a(argc, argv);
  WorkerOne worker;
  QThread thread;
  thread.start();
  worker.start();
  worker.moveToThread(&thread);
  a.connect(&thread, SIGNAL(finished()), SLOT(quit()));
  QTimer::singleShot(3456, &thread, SLOT(quit()));
  return a.exec();
}

When the timer times out, the thread's event loop quits, the thread finishes, the application's event loop is quit, and, finally, the thread and the worker get destroyed.

A zero-length timer is not really a timer, just an idiom that means: invoke me as soon as the event loop is entered and there's nothing else to do. Doing the below would be a premature pessimization as there's be a memory allocation per each round through the event loop - not using the timer would be worse!

class WorkerOne : public QObject {
  Q_OBJECT
  Q_INVOKABLE void processChunk() {
    ...
    // A lame attempt to call ourselves again from the event loop.
    // It works, but has lot more overhead than a zero-length timer!
    QMetaObject::invokeMethod(this, "processChunk", Qt::QueuedConnection);
  }
public:
  WorkerOne(QObject * parent = 0) : QObject(parent) {}
  Q_SLOT void start() {
    QMetaObject::invokeMethod(this, "processChunk", Qt::QueuedConnection);
  }
};
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top