Название: [Решено] Thread tried to wait on itself Отправлено: sergek от Март 15, 2021, 09:01 Коллеги,
в одной библиотеке встретил любопытный способ использования QThread, попробовал на примере, приведенном в справке Qt (https://doc.qt.io/qt-5/qthread.html (https://doc.qt.io/qt-5/qthread.html)). В примере перенос объекта Worker в поток осуществляется в конструкторе Controller с ожиданием его завершения в деструкторе: Код А в библиотеке, о которой я упоминал, это делается в самом Worker: Код
Но при этом формируется предупреждение "QThread::wait: Thread tried to wait on itself". Это сообщение понятно: ждать в себе завершения самого себя бессмысленно)) Но в чем принципиальная ошибочность моего кода? В присоединенном архиве - пример второго способа. Название: Re: Thread tried to wait on itself Отправлено: ssoft от Март 15, 2021, 11:42 ждать в себе завершения самого себя бессмысленно)). Это ж и есть ответ на вопрос. Вызов деструктора Worker производится в активности потока workerThread, который и предлагается подождать с помощью workerThread->wait(). В первом же варианте вызов деструктора Controller производится в активности другого (скорее всего, главного) потока, связанного с объектом Controller. Здесь можно и подождать workerThread->wait(). Название: Re: Thread tried to wait on itself Отправлено: sergek от Март 15, 2021, 12:22 Собственно, это всего лишь предупреждение, и wait в этом случае ничего не делает. Убираем wait, остается выход из циклов обработки событий quit и отложенное удаление объекта QThread с помощью deleteLater. Вопрос остается - это правильно?
Название: Re: Thread tried to wait on itself Отправлено: Igors от Март 15, 2021, 12:44 Собственно, это всего лишь предупреждение, и wait в этом случае ничего не делает. Убираем wait, остается выход из циклов обработки событий quit и отложенное удаление объекта QThread с помощью deleteLater. Вопрос остается - это правильно? Может избавиться от сообщения такКод
Название: Re: Thread tried to wait on itself Отправлено: ssoft от Март 15, 2021, 12:49 Собственно, это всего лишь предупреждение, и wait в этом случае ничего не делает. Убираем wait, остается выход из циклов обработки событий quit и отложенное удаление объекта QThread с помощью deleteLater. Вопрос остается - это правильно? Удаление объекта QThread само по себе не завершает активность (Note that deleting a QThread object will not stop the execution of the thread it manages.). Метод wait же ожидает завершения активности. В общем случае, второй вариант допускает "подвешивание" незавершенных активностей, особенно если они реализованы в виде бесконечных циклов (когда реализована собственная версия метода MyThread::run() ). Название: Re: Thread tried to wait on itself Отправлено: ssoft от Март 15, 2021, 12:54 Всё даже может быть намного хуже, если полностью прочитать документацию)).
Цитировать Note that deleting a QThread object will not stop the execution of the thread it manages. Deleting a running QThread (i.e. isFinished() returns false) will result in a program crash. Wait for the finished() signal before deleting the QThread. Название: Re: Thread tried to wait on itself Отправлено: sergek от Март 15, 2021, 12:57 Может избавиться от сообщения так Именно так и реализовано в Qt ))Название: Re: Thread tried to wait on itself Отправлено: sergek от Март 15, 2021, 13:01 Всё даже может быть намного хуже, если полностью прочитать документацию)). Если не ошибаюсь, это предупреждение об прямом удалении с помощью delete. Я ж использую deleteLater. Правда, глубоко не залезал в реализацию, а там стоит wait))Цитировать Note that deleting a QThread object will not stop the execution of the thread it manages. Deleting a running QThread (i.e. isFinished() returns false) will result in a program crash. Wait for the finished() signal before deleting the QThread. Так можно или нельзя и почему? Название: Re: Thread tried to wait on itself Отправлено: ssoft от Март 15, 2021, 13:11 Вот пример по шагам
Код
Название: Re: Thread tried to wait on itself Отправлено: Авварон от Март 15, 2021, 13:31 Но в чем принципиальная ошибочность моего кода? Так у вас телега впереди лошади. QThread - это объект-хэндл треда, а не сам тред (сам тред - это то, что в run()). Хэндл нужен для управления тредом из родительского (часто главного) треда - родительский поток запускает новый поток, он же его останавливает и дожидается. А так у вас получается что владелец потока (объект Worker) перемещен внутрь этого потока, а тред не может заджойнить сам себя. Название: Re: Thread tried to wait on itself Отправлено: sergek от Март 15, 2021, 14:00 Хэндл нужен для управления тредом из родительского (часто главного) треда - родительский поток запускает новый поток, он же его останавливает и дожидается. Спасибо за напоминание, что "хэндл" живет в потоке, в котором он создан)) Но откуда следует, что остановить поток (quit) можно только из того потока, откуда он был запущен? Название: Re: Thread tried to wait on itself Отправлено: Авварон от Март 15, 2021, 14:34 Спасибо за напоминание, что "хэндл" живет в потоке, в котором он создан)) Но откуда следует, что остановить поток (quit) можно только из того потока, откуда он был запущен? Ни откуда, quit() можно звать из любого потока. Нельзя звать wait() из потока потому что, по сути, wait() - это join() - вы ждете когда две нитки сольются в одну (ту, которая вызвала wait()/join()). Как нитка может поджойнится сама с собой? Название: Re: Thread tried to wait on itself Отправлено: sergek от Март 15, 2021, 14:53 Иначе говоря, код
Код корректен? Название: Re: Thread tried to wait on itself Отправлено: ssoft от Март 15, 2021, 15:12 Спасибо за напоминание, что "хэндл" живет в потоке, в котором он создан)) Но откуда следует, что остановить поток (quit) можно только из того потока, откуда он был запущен? Это QObject, с которым могут связаны какие-нибудь сигналы, и слоты вызываются в основном потоке. Сама обертка QThread не помечена, как thread-safe, таким образом одновременный вызов методов из разных потоков может привести к плачевным результатам. Иначе говоря, код Код корректен? Я считаю, что некорректен, и не стал бы его использовать, так как содержит потенциальную проблему - сегодня работает, а завтра ... Название: Re: Thread tried to wait on itself Отправлено: Igors от Март 15, 2021, 15:35 Иначе говоря, код Это не так уж важно :), плохо уже то что создается почва для (ненужных) раздумий. Вызов quit останавливает loop нитки, но нитка еще не завершилась. Код корректен? Цитировать Since Qt 4.8, if deleteLater() is called on an object that lives in a thread with no running event loop, the object will be destroyed when the thread finishes. Т.е. если вызов был из workerThread - сработает. А вот из др - хз. Напр вызывающий вытеснен между quit и deleteLater, а когда проснулся - нитка уже завершена Edit: а, так из другой дкструктор вызваться и не сможет Название: Re: Thread tried to wait on itself Отправлено: Авварон от Март 15, 2021, 16:13 корректен? Нет, потому что тред незаджойнен, а незаджойненые треды ведут к крашам (когда main() уже вышел, Глобал статики развалились, а тред еще жужжит) Название: Re: Thread tried to wait on itself Отправлено: sergek от Март 15, 2021, 20:10 Все понял, признаю свои ошибки, больше не буду))
Название: Re: Thread tried to wait on itself Отправлено: Igors от Март 16, 2021, 11:54 Нет, потому что тред незаджойнен, а незаджойненые треды ведут к крашам (когда main() уже вышел, Глобал статики развалились, а тред еще жужжит) Вот есть желание сделать какого-то worker'а, который по собственной инициативе (может быть) запускает workerThrеad (возможно член класса), ну и завершает/удаляет ее в деструкторе. Что тут плохого/некорректного ?Правда возникает проблемка как дождаться завершения workerThrеad не имея к ней доступа в main нитке Название: Re: Thread tried to wait on itself Отправлено: sergek от Март 16, 2021, 21:38 Правда возникает проблемка как дождаться завершения workerThrеad не имея к ней доступа в main нитке Такой вокер имеет место на существование с единственным ограничением - его удаление должно выполняться в другом потоке, т.е. не в том, где создавался Worker и workerThread. Тогда текущий поток и поток workerThread не совпадают и wait() работает.У меня это невозможно, к сожалению. Название: Re: Thread tried to wait on itself Отправлено: Igors от Март 17, 2021, 11:17 Такой вокер имеет место на существование с единственным ограничением - его удаление должно выполняться в другом потоке, т.е. не в том, где создавался Worker и workerThread. Тогда текущий поток и поток workerThread не совпадают и wait() работает. А как удалить worker'а из main если worker->thread() == workerThread ? Если ничего не путаю, получим ошибку выполнения (и это правильно)У меня это невозможно, к сожалению. Проблемка не выглядит ужасной, можно напр взводить футуру по сигналу finished (workerThread), а в main ждать на этой футуре. С др стороны затея "worker сам разбирается со своими нитками" имеет смысл Название: Re: Thread tried to wait on itself Отправлено: sergek от Март 17, 2021, 14:45 А как удалить worker'а из main если worker->thread() == workerThread ? Да никак. Controller и Worker в моем примере создаются и удаляются в main, из-за чего и проблема. Но можно добавить еще один вспомогательный поток, который должен служить только для одного - при завершении программы удалить все worker'ы. Тогда описанный подход должен сработать. Нет времени проверять)) PS. Кстати, у себя в проекте я сделал по классической схеме (менеджер потока создается в Controller), но сколько гимора из-за этого поимел (отчасти из-за небрежной иерархии принадлежности объектов), что лучше бы проверил и сделал такой вспомогательный поток)) Название: Re: Thread tried to wait on itself Отправлено: Igors от Март 18, 2021, 08:37 Но можно добавить еще один вспомогательный поток, который должен служить только для одного - при завершении программы удалить все worker'ы. Тогда описанный подход должен сработать. Зачем же нитками так разбрасываться? Можно напр ждать на футуре или семафоре, может оформить выход из главного цикла в 2 этапа, первый удаление workке'овНазвание: Re: [Решено] Thread tried to wait on itself Отправлено: sergek от Март 19, 2021, 18:41 Чтобы тема приобрела какой-то завершенный вид, я доработал пример, добавив в него вспомогательный класс WorkerPool и поток poolThread для него. Класс служит для хранения указателей на worker'ы в виде простого списка и их удаления в своем потоке (чтобы wait сработал, как надо). Здесь создаются 2 worker'а:
Код
Удаление pool выполняется по сигналу завершения вспомогательного потока finished, поток завершается в деструкторе контроллера: Код
В деструкторе пула удаляются worker'ы оператором delete (это необходимо, чтобы вызвать деструктор worker'а в потоке пула): Код
Вот такой вывод этого примера (в начале каждого сообщения - идентификатор потока: Код: 0x33E8 pool created Напомню, что идея не моя)) Если интересно, то первоисточник тут: https://github.com/StefanFrings/QtWebApp/blob/master/QtWebApp/httpserver/httpconnectionhandler.cpp (https://github.com/StefanFrings/QtWebApp/blob/master/QtWebApp/httpserver/httpconnectionhandler.cpp) Там не все очевидно, автор мне подсказал, что удаление выполняется в HttpConnectionHandlerPool::cleanup(). |