Как остановить qThread в QT

Я хотел бы знать, как правильно остановить QThread. У меня есть бесконечный цикл в потоке, и я хотел бы остановить его, когда выполняю определенное действие:

Я пытался :

if (thread->isRunning()){
    worker->stop();
    thread->terminate();
}

метод stop() устанавливает значение false, чтобы выйти из моего бесконечного цикла.

Кроме того, я действительно не понимаю разницы между quit(), terminate() или wait(). Может кто-нибудь объяснить мне?

Спасибо.


person iAmoric    schedule 08.03.2017    source источник
comment
Относительно terminate: Предупреждение: эта функция опасна, и ее использование не рекомендуется. Поток может быть завершен в любой точке его пути кода. Потоки могут быть завершены при изменении данных. У потока нет шансов очиститься после себя, разблокировать удерживаемые мьютексы и т. д. Короче говоря, используйте эту функцию только в случае крайней необходимости. источник: doc.qt.io/qt-4.8/qthread.html#terminate . В остальном, что в документации вам не понятно?   -  person Richard Critten    schedule 08.03.2017
comment
Да, я знаю, что это опасно, поэтому я хотел бы знать, как правильно остановить поток.   -  person iAmoric    schedule 08.03.2017
comment
В документации кажется, что оба метода (с выходом() тоже) сообщают циклу потока о выходе. я действительно не вижу разницы   -  person iAmoric    schedule 08.03.2017
comment
используйте переменную std::atomic<bool> или std::atomic_bool. Установите его в своем конкретном действии и протестируйте его в своей процедуре потока и аккуратно завершите работу.   -  person Richard Critten    schedule 08.03.2017
comment
См. также этот ответ, в котором подробно описано, как преобразовать бесконечный цикл, который у вас есть, в цикл, который взаимодействует с системой событий и слотами в очереди. и, таким образом, является более гибким и простым в использовании.   -  person Kuba hasn't forgotten Monica    schedule 08.03.2017


Ответы (1)


Правильный ответ зависит от того, как вы на самом деле используете QThread и как вы реализовали stop().

Предполагаемый вариант использования в Qt предполагает следующую модель:

  • Вы создаете объект, который будет выполнять некоторую полезную работу в ответ на Сигналы.
  • Вы создаете `QThread` и перемещаете свой объект в этот поток
  • Когда вы отправляете сигнал своему объекту, он обрабатывается в созданном вами QThread.

Теперь вам нужно понять некоторые внутренности того, как это на самом деле реализовано. В Qt есть несколько «моделей» сигналов, и в некоторых случаях, когда вы «отправляете сигнал», вы фактически просто вызываете функцию «слот». Это "прямое" подключение к слоту, и в этом случае slot() будет выполняться в вызывающем потоке, который поднял сигнал. Таким образом, для связи с другим потоком Qt допускает другой тип сигналов, соединения в очереди. Вместо вызова slot() вызывающий объект оставляет сообщение объекту, которому принадлежит этот слот. Поток, связанный с этим объектом, прочитает это сообщение (через некоторое время) и выполнит выполнение самого slot().

Теперь вы можете понять, что происходит, когда вы создаете и выполняете QThread. Вновь созданный поток будет выполнять QThread::run(), который по умолчанию будет выполнять QThread::exec(), который представляет собой не что иное, как бесконечный цикл, который ищет сообщения для объектов, связанных с потоком, и передает их в слоты этих объектов. При вызове QThread::quit() в эту очередь отправляется сообщение о завершении. Когда QThread::exec() прочитает его, он остановит дальнейшую обработку событий, выйдет из бесконечного цикла и аккуратно завершит поток.

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

  • Вы должны запустить `QThread::exec()`
  • Вы должны выйти из слота, который в данный момент работает

Первый обычно нарушается, когда люди подклассируют QThread и переопределяют QThread::run своим собственным кодом. В большинстве случаев это неправильное использование, но оно все еще очень широко преподается и используется. В вашем случае кажется, что вы нарушаете второе требование: ваш код выполняет бесконечный цикл, и поэтому QThread::exec() просто не получает управления и не имеет возможности проверить, что ему нужно выйти. Отбросьте этот бесконечный цикл в корзину, QThread::exec() уже выполняет такой цикл для вас. Подумайте, как переписать свой код, чтобы он не запускал бесконечных циклов, это всегда возможно. Подумайте о своей программе с точки зрения концепции «сообщения в поток». Если вы периодически что-то проверяете, создайте QTimer, который будет отправлять сообщения вашему объекту, и реализуйте проверку в своем слоте. Если вы обрабатываете большой объем данных, разделите эти данные на более мелкие фрагменты и напишите свой объект, чтобы он обрабатывал по одному фрагменту за раз в ответ на какое-либо сообщение. Например. если вы обрабатываете изображение построчно, создайте слот processLine(int line) и отправьте в этот слот последовательность сигналов "0, 1, 2... height-1". Обратите внимание, что вам также придется явно вызывать QThread::quit() после завершения обработки, потому что цикл событий бесконечен, он не «знает», когда вы обработали все строки вашего изображения. Также рассмотрите возможность использования QtConcurrent для ресурсоемких задач вместо QThread.

Теперь QThread::terminate() останавливает поток совсем другим способом. Он просто просит ОС убить ваш поток. И ОС просто резко остановит ваш поток в произвольной позиции кода. Память стека потока будет освобождена, но любая память, на которую этот стек указывает, не будет освобождена. Если поток владел каким-либо ресурсом (например, файлом или мьютексом), он никогда не освободит его. Операция, связанная с записью данных в память, может быть остановлена ​​посередине и оставить блок памяти (например, объект) незаполненным и в недопустимом состоянии. Как вы можете догадаться из этого описания, вы никогда не должны вызывать ::terminate(), за исключением очень редких случаев, когда сохранение работы потока хуже, чем утечка памяти и ресурсов.

QThread::wait() — это просто вспомогательная функция, которая ждет, пока QThread перестанет выполняться. Он будет работать как с exit(), так и с terminate().

Вы также можете реализовать собственную систему многопоточности, унаследованную от QThread, и реализовать собственную процедуру завершения потока. Все, что вам нужно для выхода из потока, это, по сути, просто вернуться из QThread::run(), когда это станет необходимо, и вы не можете использовать для этой цели ни exit(), ни terminate(). Создайте свой собственный примитив синхронизации и используйте его, чтобы сообщить вашему коду о возврате. Но в большинстве случаев это не очень хорошая идея, имейте в виду, что (если вы не работаете с QEventLoop самостоятельно) сигнал Qt и слоты не будут работать должным образом в этом случае.

person Sergei Ozerov    schedule 08.03.2017