Подтвердить что ты не робот

Количество потоков увеличивается, даже при удалении потоков

Имейте приложение, в котором у меня есть 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). Когда больше нет загружаемых файлов, файл-загрузчик сигнализирует "finished()", который вызывает deleteFinishedThread и deleteFinishedUploader, где я это делаю:

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, которые я удаляю, и все, кажется, удаляется, но все же я царапаю себе голову, когда количество строк продолжает увеличиваться...

EDIT: В моем файловом загрузчике я создаю объект (диспетчер) в куче, в котором есть стек QNetworkAccessManager. Когда файловый загрузчик удаляется, он вызывает "deleteLater()" в Менеджере, но он никогда не удаляется. Мы попытались удалить Диспетчер и установить его в NULL, но это дало нам нарушение доступа, так как Менеджер еще не был выполнен (QNetwork.dll сообщила об ошибке, так что это должно быть что-то внутри QNAM, который все еще работает). Время, когда мы не получили нарушение доступа, объект был удален, и количество потоков вернулось к нормальному состоянию. Что может жить внутри QNAM и мешать мне удалять его, когда он выходит за рамки? Должен ли я создать QNAM вместо кучи? На этом этапе не деструкторы вызываются даже при вызове функции deleteLater()...

Также, как уменьшить количество меток?

4b9b3361

Ответ 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 секунд или около того (несколько "потоков наблюдателей" должны быть убиты окнами и т.д.).

Ответ 2

Возможно, я ошибаюсь, но я думаю, что есть проблемы с вашими сигналами:

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

Помните, что когда несколько слотов подключены к одному и тому же сигналу, они выполняются в порядке соединения. Здесь, когда файл-загрузчик будет завершен, он вызовет finished(), который сначала вызовет метод quit() потока, а затем метод deleteFinishedUploader(). То же самое для сигнала canceled(). Но, тем временем, поток был закончен, поэтому обработка событий для fileUploader не может быть выполнена (следствие moveToThread(...)). deleteLater() нужна обработка событий, поэтому ваш файловый загрузчик никогда не будет удален...

Я не на 100%, что упорядочение ваших подключений по-другому приведет к работе: можно вызвать deleteLater() и поток сразу после него, без обработки событий.

Решение может состоять в том, чтобы вернуть файл-загрузчик в основной поток или в поток, который все еще обрабатывает цикл событий.

Ответ 3

Не ответ, но:

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

означает, что вы начинаете четный цикл, а затем вы выстраиваете в очередь слоты или сигналы, которые нужно выполнить. Предположим (в общем), что в этом потоке есть другие QObject. Возможно, они получат свои слоты или сигналы, выполненные, потому что цикл событий уже запущен. Если вы хотите, чтобы "init" и "uploadAt" были первыми методами, вызываемыми при запуске цикла событий, вы ставите их в очередь перед запуском цикла событий (если поток не запущен, они никогда не будут исполняться).

От QMetaObject:: invokeMethod:

Если тип Qt:: QueuedConnection, QEvent будет отправлен, и член будет вызван, как только приложение войдет в основной цикл событий.

В этом случае событие отправляется в цикл событий потока.