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

chi*_*uba 13 c++ qt multithreading 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++;
Run Code Online (Sandbox Code Playgroud)

每个线程都以列表索引开始,以便他们可以获取他们需要上传的内容并继续执行大约5个步骤(使用QNetworkAccessManager调用).当没有更多的项目上载的fileUploader信号"已完成()",呼吁在deleteFinishedThreaddeleteFinishedUploader在那里我做的:

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

if(thread != NULL) thread->deleteLater();
Run Code Online (Sandbox Code Playgroud)

要么

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

if(fileUploader != NULL) fileUploader->deleteLater();
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

另外,如何减少句柄数?

Syn*_*xis 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);
Run Code Online (Sandbox Code Playgroud)

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

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

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


chi*_*uba 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()));
Run Code Online (Sandbox Code Playgroud)

这意味着当对象被删除时,线程将停止(quit())。尽管文档指出,这实际上是有效的:

该信号在对象 obj 被销毁之前立即发出,并且无法被阻止。

该信号发出后,所有对象的子对象都会立即被销毁。

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