C++ 保持活动状态的线程(Qt)

C++ 保持活动状态的线程(Qt),c++,multithreading,qt,C++,Multithreading,Qt,我正在尝试纠正一个大型程序的内存泄漏和未停止的线程。我知道我有一些,但我不确定如何正确识别和杀死它们,所以我开始玩一些经典的例子,我已经有了 首先,我尝试了最简单的方法: int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); return a.exec(); } 这使我在任务管理器中有一(1)个正在运行的线程 int main(int argc, char *argv[]) { QCoreA

我正在尝试纠正一个大型程序的内存泄漏和未停止的线程。我知道我有一些,但我不确定如何正确识别和杀死它们,所以我开始玩一些经典的例子,我已经有了

首先,我尝试了最简单的方法:

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    return a.exec();
}
这使我在任务管理器中有一(1)个正在运行的线程

int main(int argc, char *argv[]) {
    QCoreApplication a(argc, argv);
    WorkerOne *w = new WorkerOne();
    QTimer::singleShot(3456, w, SLOT(stop()));
    return a.exec();
}
这一个在启动辅助线程之前给我1,然后在线程实际启动之前给我2(
process
被调用),然后给我3,直到捕捉到
singleShot
信号并删除辅助线程,然后再给我2。所以我有点松了

这是工作指南的代码:

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) { }
平台是Qt5.2.1,带有mingw48_32编译器


我想我正在按照中的步骤进行操作,但可能我遗漏了什么。

您对worker对象的实现实际上是颠倒的。旋转事件循环是QThread的工作。您的工作对象应该只由插槽调用和传入事件驱动。处理忙循环习惯用法使用零长度计时器保持活动状态,同时允许事件循环接收事件并退出,而不需要额外的标志

以下是如何做到这一点:

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();
}
当计时器超时时,线程的事件循环退出,线程完成,应用程序的事件循环退出,最后,线程和工作线程被销毁

零长度计时器并不是真正的计时器,它只是一个习语,意思是:一旦进入事件循环就调用我,而无需其他操作。这样做未免过早悲观,因为在事件循环中每轮都有内存分配——不使用计时器会更糟糕

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);
  }
};

第一个线程用于
main
,第二个线程用于
WorkerOne
,但谁在这里创建了第三个线程?我测试了您的代码,当我将单次触发计时器的超时从
1000
更改为
2000
时,线程数从
3
变为预期的
2
。出于某种原因,
QTimer::singleShot
正在启动第三个线程,但不是在我将超时增加到2000时。我不知道那里发生了什么。实际上它应该被删除,因为文档中说明了
QThread::finished
信号:。@thuga这里它根本不适用,因为有一个从
finished()
deleteLater()
的直接连接,它将被线程的事件循环拾取。我误读了代码。在其他工人仍在工作的更一般情况下,我不会退出QCoreApplication。删除该连接的代码永远不会调用
WorkerOne
的析构函数,并且需要从
&thread
finished()
发送信号到
WorkerOne
deleteLater()
。是吗?@dmcontador这些都是局部变量,它们没有分配到堆上。只要
main()
返回,就可以保证
WorkerOne
QThread
都会按以下顺序被破坏。在这些对象中的任何一个上调用
deleteLater
,实际上都是一个错误。一般来说,除非需要,否则不要在堆上分配东西。对于
QObject
类型的类成员,欢迎将它们作为直接类成员,而不是指针成员。@KubaOber将在主线程或新线程中调用
processChunk
(因为
worker.start
worker.moveToThread
之前启动)?