即使删除线程,线程数也会增加很多

时间:2012-04-17 02:46:45

标签: c++ multithreading qt qnetworkaccessmanager

有一个我有QOBJects的应用程序,它们都包含QNetworkAccessManager。我知道它建议只针对每个应用程序,但由于我在同一时间做了6个以上的调用,我需要这样做。所以,这就是我启动线程的方式。

FileUploader *fileUploader = new FileUploader(_fileList);
QThread *fileUploaderThread = new QThread();
fileUploader->moveToThread(fileUploaderThread);

// uploader > model
connect(fileUploader, SIGNAL(progressChangedAt(int)), _model, SLOT(reportProgressChanged(int)), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(statusChangedAt(int)), _model, SLOT(reportStatusChanged(int)), Qt::QueuedConnection);
// uploader > its thread
connect(fileUploader, SIGNAL(canceled()), fileUploaderThread, SLOT(quit()), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(finished()), fileUploaderThread, SLOT(quit()), Qt::QueuedConnection);
// uploader > this
connect(fileUploader, SIGNAL(canceled()), this, SLOT(deleteFinishedUploader()), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(finished()), this, SLOT(deleteFinishedUploader()), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(finishedCurrentUpload()), this, SLOT(uploadNextFileOrFinish()), Qt::QueuedConnection);
// thread > this
connect(fileUploaderThread, SIGNAL(finished()), this, SLOT(checkIfAllThreadsAreFinished()), Qt::QueuedConnection);
connect(fileUploaderThread, SIGNAL(finished()), this, SLOT(deleteFinishedThread()), Qt::QueuedConnection);
// this > uploader
connect(this, SIGNAL(cancel()), fileUploader, SLOT(cancel()), Qt::QueuedConnection);

fileUploaderThread->start();
QMetaObject::invokeMethod(fileUploader, "init", Qt::QueuedConnection);
QMetaObject::invokeMethod(fileUploader, "uploadAt", Qt::QueuedConnection, Q_ARG(int, startIndex));

QMutexLocker locker(&_mutex);
_threadCount++;

每个线程都以列表索引开始,以便他们可以获取他们需要上传的内容并继续执行大约5个步骤(使用QNetworkAccessManager调用)。当没有更多要上传的项目时,fileUploader会发出“finished()”信号,它会调用我所在的deleteFinishedThreaddeleteFinishedUploader

QThread *thread = qobject_cast<QThread*>(sender());

if(thread != NULL) thread->deleteLater();

FileUploader *fileUploader = qobject_cast<FileUploader*>(sender());

if(fileUploader != NULL) fileUploader->deleteLater();

这些假设是在完成后删除线程。

问题在于每次我启动(例如)3个线程,其中有1个文件要上传并处理每个文件,线程数增加8-10。这意味着如果我重新启动上载过程几次,则线程数从大约5到100。

我做错了什么?或者是我使用“Windows任务管理器”来控制它的最大问题?我正在处理我删除的QNAM的所有回复,一切似乎都被删除了,但是当线程数不断增加时,我仍然摸不着头脑......

编辑: 在我的fileuploader中,我在堆上创建了一个对象(Manager),它在堆栈上有一个QNetworkAccessManager。当文件上传器被删除时,它会在Manager上调用“deleteLater()”但它永远不会被删除。我们尝试删除管理器并将其设置为NULL,但由于管理器尚未完成(QNetwork.dll报告了问题所以它必须是QNAM中仍在运行的内容),因此这给了我们访问冲突。我们没有获得访问冲突的时间,对象被删除,线程数恢复正常。什么可以存在于QNAM内部并阻止我在超出范围时将其删除?我应该在堆上创建QNAM吗?在此阶段,即使在调用deleteLater()...

时也会调用非析构函数

另外,如何减少句柄数?

3 个答案:

答案 0 :(得分:7)

我可能错了,但我认为您的信号存在问题:

// uploader > its thread
connect(fileUploader, SIGNAL(canceled()), fileUploaderThread, SLOT(quit()), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(finished()), fileUploaderThread, SLOT(quit()), Qt::QueuedConnection);
// uploader > this
connect(fileUploader, SIGNAL(canceled()), this, SLOT(deleteFinishedUploader()), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(finished()), this, SLOT(deleteFinishedUploader()), Qt::QueuedConnection);

请记住,当多个插槽连接到同一个信号时,它们会按连接顺序执行。这里,当fileUploader完成时,它将调用finished(),它将首先调用线程的quit()方法,然后调用deleteFinishedUploader()方法。 canceled()信号相同。 但是,同时,线程已完成,因此无法完成fileUploader的事件处理(moveToThread(...)的结果)。 deleteLater()需要事件处理,因此永远不会删除您的fileUploader ...

我不是百分之百,以另一种方式安排你的连接将使事情正常工作:可以调用deleteLater()并且线程在没有事件处理的情况下立即退出。

解决方案可能是将fileUploader重新moveToThread()到主线程,或者重新处理仍然处理其事件循环的线程。

答案 1 :(得分:2)

不是答案,但是:

    fileUploaderThread->start();
    QMetaObject::invokeMethod(fileUploader, "init", Qt::QueuedConnection);
    QMetaObject::invokeMethod(fileUploader, "uploadAt", Qt::QueuedConnection, Q_ARG(int, startIndex));

表示启动偶数循环,然后排队要执行的插槽或信号。假设(通常)此线程中还有其他QObject。由于事件循环已经开始,因此可能会执行这些插槽或信号。如果希望“init”和“uploadAt”成为事件循环运行时调用的第一个方法,则在启动事件循环之前对它们进行排队(如果线程未启动,则永远不会执行它们。) / p>

来自QMetaObject::invokeMethod

  

如果type是Qt :: QueuedConnection,则会发送一个QEvent,并在应用程序进入主事件循环后立即调用该成员。

在这种情况下,事件被发送到线程事件循环。

答案 2 :(得分:1)

在经历了很多“几乎放弃”之后,我想出了一个解决方案。 Synxis对插槽顺序的说法确实如此。

然而,我仍然对文件句柄有一些问题,如果有人有更好的答案,我很乐意接受。

我将代码更改为:

...
// uploader > this
connect(fileUploader, SIGNAL(canceled()), this, SLOT(deleteFinishedUploader()), Qt::QueuedConnection);
connect(fileUploader, SIGNAL(finished()), this, SLOT(deleteFinishedUploader()), Qt::QueuedConnection);
// uploader > its thread
connect(fileUploader, SIGNAL(destroyed()), fileUploaderThread, SLOT(quit()));

这意味着当对象被删除时线程被停止(quit())。即使文档声明:

,这实际上也有效
  

该信号在物体obj被破坏之前立即发射,并且不能被阻挡。

     

发出此信号后,所有物体的子物都会立即被摧毁。

这意味着这些信号会在任何东西被破坏之前发出(这意味着我会在删除它的上传器之前退出该线程)?不是很好,它可能是一个更好的方式。但是,atm,每次上传器完成后我的线程数会下降很多,并且在20秒左右后恢复正常(一些“观察者线程”必须被Windows等杀死)。