LINUX.ORG.RU

Сегфолт при завершении работы потока QThread - что не так?

 ,


0

2

Есть класс окна, в полях которого имеются два свойства:

 
protected:
    QThread moveDetectorThread; // Объект потока
    MoveDetector moveDetector; // Некий объект для запуска в потоке


В конструкторе есть следующий код, и он нормально запускает объект в потоке:

    // Запуск цикла объекта при старте потока
    connect(&moveDetectorThread, &QThread::started, &moveDetector, &MoveDetector::run);
    
    // Соединения для корректного завершения потока
    connect(&moveDetector, SIGNAL(finished()), &moveDetectorThread, SLOT(quit()));
    connect(&moveDetector, SIGNAL(finished()), &moveDetector, SLOT(deleteLater()));
    connect(&moveDetectorThread, SIGNAL(finished()), &moveDetectorThread, SLOT(deleteLater()));
    
    moveDetector.moveToThread(&moveDetectorThread); //  Объект переносится в тред
    moveDetectorThread.start(); // Тред запускается


Сам объект имеет метод основного цикла и метод, устанавливающий флаг выхода:

void MoveDetector::run()
{
    exitFlag=false;

    for(;;){
        update();
        if(exitFlag) {
            emit finished();
            return;
         }
    }
}


void MoveDetector::doExit()
{
    exitFlag=true;
}


В деструкторе окна я написал такой код:

    moveDetector.doExit();
    while(!moveDetectorThread.isFinished()) {
        qDebug() << "Wait finished move detector...";
    }


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

Wait finished move detector...
                 [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Wait finished move detector...
Wait finished move detector...
Wait finished move detector...
The program has unexpectedly finished.
Процесс был завершён принудительно.
/home/xi/work/develop/cpp/MoveNoid/build-MoveNoid-Desktop_Qt_5_9_2_GCC_64bit-Debug/MoveNoid crashed.


Информацию брал вот отсюда: https://habrahabr.ru/post/150274/

Вопрос: почему крешится программа? Что сделать чтоб поток нормально завершался?

UPD: Сделал минимальный пример: http://rgho.st/8dD7n4ljc
При нажатии Stop все зависает, окно перестает отвечать.

★★★★★

Последнее исправление: Xintrea (всего исправлений: 3)

почему крешится программа?

Потому что программа обращается по невалидному адресу // К.О.

Невалидный адрес, наверное, получается после завершения нити.

Что сделать чтоб поток нормально завершался?

Писать нормально. Ожидать завершения нити через pthread_join или как там в Qt.

anonymous
()

::run()

exitFlag=true;

Обнаружено неправильное приготовление QThread. Не так его на бутерброд намазывают, потому и проблемы с корректным завершением.

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

exitFlag тут используется только для того, чтобы по завершению update() отправить сигнал finished() и выпрыгнуть из бесконечного цикла.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от I-Love-Microsoft

По сути, вот здесь тоже самое в методе run() происходит:

https://evileg.com/ru/post/152/

только переменная m_running называется:

void ExampleObject::run()
{
    count = 0;
    // Переменная m_running отвечает за работу объекта в потоке.
    // При значении false работа завершается
    while (m_running)
    {
        count++;
        emit sendMessage(m_message); // Высылаем данные, которые будут передаваться в другой поток
        qDebug() << m_message << " " << m_message_2 << " " << count;
    }
    emit finished();
}

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Я поспешил с выводами. QThread используется примерно в правильном ключе, но возможно взаимодействие через doExit это не очень.

Почему бы не QThread::wait? У тебя же &moveDetectorThread, SLOT(quit() + moveDetector делитится. Вот там шарики за ролики едут.

Попробуй moveDetectorThread.wait();

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Приложил минимальный пример, чтоб обсуждение было более предметным.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от I-Love-Microsoft

Сделал так:

    moveDetector.doExit();
    moveDetectorThread.wait();
    qDebug() << "Success finish.";

Поток останавливается, но окно перестает отвечать. И сообщения о завершении нет.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Да, я как-то мимо, не в попад отвечаю. Вот что тут в update? Там есть обработка сообщений цикла Qt для этого потока?

    for(;;){
        update();
        if(exitFlag) {
            emit finished();
            return;
         }
    }
Помимо потоконебезопасного doExit, не вижу работы с event loop в этом бесконечном цикле.

I-Love-Microsoft ★★★★★
()

а ты уверен что finished надо посылать самому?

ckotinko ☆☆☆
()

во вторых, у QThread есть метод wait, который ждет завершения потока.

в третьих, isFinished приходит РАНЬШЕ завершения потока, барашка моя. и ты удаляешь QThread до его завершения.

ckotinko ☆☆☆
()
Ответ на: комментарий от ckotinko

Код ТС-а у меня стал нормально работать когда я сделал так:

#include <QThread>
...
void MoveDetector::run()
{
    exitFlag=false;

    for(;;){
        update();

        if(exitFlag) {
            emit finished();
            this->thread()->quit(); // типа вот
            return;
         }
    }
}

I-Love-Microsoft ★★★★★
()

MoveDetector

Не по сабжу: я чтоб поток не создавать каждый раз, делал на cond Variable.

Программа проверяет, есть ли движение и читает датчики (через ардуино). Умеет писать видео, слать е-маил, телеграм, MJPEG и обмениваться с другими программами через зашифрованный трафик. (чтоб можно было запустить на сервере).

ymuv ★★★★
()
Ответ на: комментарий от I-Love-Microsoft

Но это все равно какая-то дичь. Что-то не так, не хорошо. Этот объект с таким вот бесконечным слотом не дает системе сообщений Qt ни вздохнуть ни пернуть. Ни даже обработать его finished, и прочие quit.

Я обычно решаю задачу так, что в QObject, который посадили в QThread, имеет свой таймер singleShot, и также может выполнять слоты, задействованные через сигналы из других потоков.

А у ТС-а бесконечный грузящий ядро цикл, не дающий потоку обработать другие события. Только явный this->thread()->quit() помогает очухаться. Не хорошо.

Вот как правильно работать с таким грузиловом в QThread? Есть ли аналог QApplication::processEvents для потока?

I-Love-Microsoft ★★★★★
()
Последнее исправление: I-Love-Microsoft (всего исправлений: 1)
Ответ на: комментарий от ymuv

Пример:

void CMainMotionFunctions::doMotionAlgoAsync(
...
std::atomic_bool& isFinished,
...
)
{
#ifdef __BUILD_MOTION_ALGO__
    while (1)
    {
        std::unique_lock<std::mutex> condVariableLock(mCondVariableMutex);
        condVar.wait(condVariableLock);

        if (isFinished.load())
        {
            isFinished.store(false);
            doMotionAlgo(
                alert,
                bufferId,
                algorithms,
                isMotionAlertFromServer,
                isSensorAlertFromServer,
                imShowPrefix,
                isShow);

            isFinished.store(true);
        }
    }
#endif
}



Тот кто запускает:

                if (mIsMotionDetectionFinished.load())
                {
                    if (!mIsRunMotionLoop)
                    {
                        mIsMotionDetectionFinished.store(true);
                        std::thread thread(
                            CMainMotionFunctions::doMotionAlgoAsync, ...., mIsRunMotionLoop, ... );
                        thread.detach();
                        mIsRunMotionLoop = true;
                    }
                    else
                    {
                        if (mIsMotionDetectionFinished.load())
                        {
                            mCondVariable.notify_one();
                        }
                    }
                }

ymuv ★★★★
()

вообще конечно эпическое рукожопие. отвлёкся от работы чтоб посмотреть по существу, а там....

exitFlag=false;

в методе run(), ага. вопрос: у тебя exitFlag хоть volatile - уже нет смысла задавать. уже понятно.

Далее:

// Соединения для корректного завершения потока
Юconnect(&moveDetector, SIGNAL(finished()), &moveDetectorThread, SLOT(quit()));
Вот хочется что-то сказать но тут я вижу вот это:
protected:
    ...
    MoveDetector moveDetector; // Некий объект для запуска в потоке

connect(&moveDetector, SIGNAL(finished()), &moveDetector, SLOT(deleteLater()));
И понимаю, что ты просто в Qt не шаришь. Вот просто пздц.

Убери свои сигналы нахер. Просто делай wait() потока в деструкторе своего класса. И сделай waitFlag volatile.

ckotinko ☆☆☆
()
Ответ на: комментарий от I-Love-Microsoft

в его случае вообще непонятно как эта шайтан-арба работает. вроде сто раз описано как правильно делать. знаешь почему wait не помогает? потому что этот чел сперва сигналом started засылает управление в moveDetector::work(), потом эта функция завершает работу и управление передается в QThread::run() который тупо ждёт. конечно оно не завершается. если делать через жопу, еще и не такое может произойти.

ckotinko ☆☆☆
()
Последнее исправление: ckotinko (всего исправлений: 1)

Во-первых, тебе правильно написал скотинка про вызов deleteLater у члена класса.

Во-вторых, если хочешь сидеть в бесконечном цикле, то тебе нужно наследоваться от QThread и переопределять метод run(). Но тогда у тебя не будет event loop и соответственно сигналов и слотов.

В-третьих, для прерывания длительных операций в qt есть специальная вещь http://doc.qt.io/qt-5/qthread.html#isInterruptionRequested. Не надо ничего колхозить.

Лучше подробнее расскажи про задачу, тогда станет ясно, как лучше сделать вынос в отдельный поток.

ox55ff ★★★★★
()

deleteLater() для мембера по значению - причина твоего краша.

moveToThread - это правильное использование многопоточности в Qt. Но в этом случае перемещаемые объекты должны быть «одноразовыми». Т.е. создал объект без парента, переместил его в тред, и через QueuedConnection вызываешь его слот, который и будет работать в этом потоке. По завершению слота грохаешь объект (можно deleteLater() у this вызвать). Но умирать должен именно объект, а не тред.

some-body ★★
()
Последнее исправление: some-body (всего исправлений: 1)
Ответ на: комментарий от I-Love-Microsoft

Проблему примерно опишу так: у меня висел бесконечный цикл, и обработки событий самого потока не происходила. При установке флага exitFlag, метод run() завершал работу, и поток начинал обрабатывать свои события. Но начинал это делать не сразу, а отложено, потому что сразу установка флага exitFlag выход из run() не делает.

Поэтому достаточно было такого завершения:

moveDetector.doExit();
moveDetectorThread.quit();
moveDetectorThread.wait();


Тут, по сути, важен quit(), который помещает сигнал завершения в цикл обработки событий потока. И ждется это дело с помощью wait().

Xintrea ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.