Russian Qt Forum
Ноябрь 22, 2024, 20:21 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3   Вниз
  Печать  
Автор Тема: Как завершить поток таймера?  (Прочитано 19766 раз)
Bepec
Гость
« Ответ #15 : Август 30, 2017, 17:34 »

Вы правы, после запуска потока deleteLater просто не работает.
Видимо у него нет критериев для QThread, по которым он может освободить объект. Что весьма странно, ведь в старых версиях утечек памяти у меня не было.
Зато простой delete работает на отлично.


Нашёл в исходниках вот такую строчечку
Цитировать
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.

и вот такую
Цитировать
 From Qt 4.8 onwards, it is possible to deallocate objects that
    live in a thread that has just ended, by connecting the
    finished() signal to QObject::deleteLater().

Могу ошибаться, но получается из 1 цитаты, что объект потока удалится лишь после окончания работы потока, в которым был вызван ( основного потока).
А из второй безболезненное решение - вешать finished на deleteLater QObject'ов, которые живут в потоке, в том числе и таймеров.

PS но вопрос открыт - как же не "ручным" способом удалять поток Улыбающийся
« Последнее редактирование: Август 30, 2017, 17:41 от Bepec » Записан
Bepec
Гость
« Ответ #16 : Август 30, 2017, 17:57 »

Пока у меня только вариант вешать на finished потока слот с delete sender'a.
Записан
demaker
Птица говорун
*****
Offline Offline

Сообщений: 962


Просмотр профиля
« Ответ #17 : Декабрь 12, 2017, 15:02 »

Ответ = вызов в нём deleteLater и quit.

Предположим, что deleteLater() для экземпляра thread отработал, это значит еще не был завершен его метод run() и exec() в другом потоке, что может быть чревато последствиями - QThread: Destroyed while thread is still running и все что описано ранее в примере 1.
Если же сначала завершить поток, а затем вызвать deleteLater(), то экземпляр thread не будет удален совсем - утечки памяти.

Простой пример

Код
C++ (Qt)
class Thread
: public QThread
{
public:
   ~Thread ()
   {
       ::std::cout << "Deleted" << ::std::endl;
   }
 
   virtual void run ()
   {
       ::std::cout << "Before exec" << ::std::endl;
       exec();
       ::std::cout << "After exec" << ::std::endl;
   }
};
 
int main ( int argc, char ** argv )
{
   QCoreApplication app( argc, argv );
 
   QThread * thread = new Thread;
   thread->moveToThread( thread );
   thread->start();
   thread->deleteLater();
   thread->quit();
 
   return app.exec();
}
 

выводит только

Код:
Before exec
After exec

либо, если между deleteLater() и quit() будет достаточно времени (например, пауза или логика какая)

Код:
Before exec
Deleted
QThread: Destroyed while thread is still running
After exec


Поправьте меня - возможно я не прав, но
возможно так лучше будет
Код
C++ (Qt)
thread->quit();
thread->wait();
delete thread;
 

И тогда вот этого не будет
Код:
QThread: Destroyed while thread is still running
Записан
Kurles
Бывалый
*****
Offline Offline

Сообщений: 480



Просмотр профиля
« Ответ #18 : Декабрь 12, 2017, 16:20 »

По мне дык наследование от QThread и переопределение в нем метода run() не очень хорошая практика. Использую механизм "воркеров", как то всё прозрачней получается, и проще следить за временем жизни объектов. Вот синтетический пример:

Код
C++ (Qt)
#include <QCoreApplication>
#include <QThread>
#include <QTimer>
#include <QDebug>
 
class Worker : public QObject {
   Q_OBJECT
public:
   Worker(QObject *parent = 0) : QObject(parent), mTimer(0) {}
   ~Worker() { qDebug() << Q_FUNC_INFO; }
 
   Q_INVOKABLE void start() {
       // для того, что бы создать таймер в контексте потока, в который перемещён
       // воркер, используем QMetaObject::invokeMethod
       if (QThread::currentThread() != thread()) {
           QMetaObject::invokeMethod(this, "start", Qt::QueuedConnection);
           return;
       }
       mTimer = new QTimer(this);
       connect(mTimer, &QTimer::timeout,
               this, &Worker::onTimeout);
       mTimer->start(1000);
   }
 
signals:
   void timeout();
 
private:
   void onTimeout() {
       qDebug() << "worker" << QThread::currentThreadId();
       emit timeout();
   }
 
private:
   QTimer *mTimer;
 
};
 
class Test : public QObject
{
   Q_OBJECT
public:
   Test(QObject *parent = 0) :
       QObject(parent),
   mWorker(new Worker())
   {
   connect(mWorker, &Worker::timeout,
           this, &Test::onTimeout,
           Qt::QueuedConnection);
   }
   ~Test()
   {
       // планируем удаление воркера
       mWorker->deleteLater();
       if (mThread.isRunning()) {
           // рубим поток
           mThread.quit();
           // даём время уничтожится воркеру
           mThread.wait();
       }
       qDebug() << Q_FUNC_INFO;
   }
 
   void start() {
       // перемещаем воркер в отдельный поток
       mWorker->moveToThread(&mThread);
       // стратуем поток
       mThread.start();
       // и воркер
       mWorker->start();
       // планируем через 5 секунд выход из тестового приложения
       QTimer::singleShot(5000, this, &Test::onQuitTimeout);
   }
 
 
   void onTimeout() {
       qDebug() << "test" << QThread::currentThreadId();
   }
 
   void onQuitTimeout() {
       qApp->quit();
   }
 
private:
   QThread mThread;
   Worker *mWorker;
 
};
 
int main(int argc, char *argv[])
{
   QCoreApplication a(argc, argv);
 
   Test t;
   t.start();
 
   return a.exec();
}
 
#include "main.moc"
 
 
Записан

Код
C++ (Qt)
while(!asleep()) sheep++;
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #19 : Декабрь 12, 2017, 16:42 »

Здесь смущает только место использования

Код
C++ (Qt)
mWorker->deleteLater();
 

Если поток уже завершен, то event loop уже остановлена и некому выполнять deleteLater).
А так, использование Worker в разных вариациях предпочтительнее. Наследование от потока тоже корректно, здесь на вкус и цвет, кому как удобнее.
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #20 : Декабрь 12, 2017, 16:48 »

Поправьте меня - возможно я не прав, но
возможно так лучше будет
Код
C++ (Qt)
thread->quit();
thread->wait();
delete thread;
 

И тогда вот этого не будет
Код:
QThread: Destroyed while thread is still running


Так тоже будет корректно, но тогда уж лучше сразу переменную на стеке делать и не мучаться с new/delete.

Записан
Kurles
Бывалый
*****
Offline Offline

Сообщений: 480



Просмотр профиля
« Ответ #21 : Декабрь 12, 2017, 17:02 »

Здесь смущает только место использования

Код
C++ (Qt)
mWorker->deleteLater();
 

Если поток уже завершен, то event loop уже остановлена и некому выполнять deleteLater).
А так, использование Worker в разных вариациях предпочтительнее. Наследование от потока тоже корректно, здесь на вкус и цвет, кому как удобнее.
Поток останавливается ниже в деструкторе. Вызов QThread::quit и последующий вызов QThread::wait() гарантирует как минимум еще одну итерацию event loop потока, которая в свою очередь обеспечит удаление все запланированных к удалению объектов, привязанных к данному потоку.
Записан

Код
C++ (Qt)
while(!asleep()) sheep++;
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #22 : Декабрь 12, 2017, 17:17 »

последующий вызов QThread::wait() гарантирует как минимум еще одну итерацию event loop потока
wait ничего не гарантирует, даже более он сам блокирует поток в котором запущен до завершения ожидаемой нитки. В это время eventloop не крутиться.
Записан
Kurles
Бывалый
*****
Offline Offline

Сообщений: 480



Просмотр профиля
« Ответ #23 : Декабрь 12, 2017, 17:36 »

последующий вызов QThread::wait() гарантирует как минимум еще одну итерацию event loop потока
wait ничего не гарантирует, даже более он сам блокирует поток в котором запущен до завершения ожидаемой нитки. В это время eventloop не крутиться.
то есть связка последовательных вызовов QThread::quit + QThread::wait не гарантирует итерации eventLoop'а? Как тогда корректно завершить поток с удалением всех его children'ов?
Записан

Код
C++ (Qt)
while(!asleep()) sheep++;
demaker
Птица говорун
*****
Offline Offline

Сообщений: 962


Просмотр профиля
« Ответ #24 : Декабрь 12, 2017, 18:01 »

последующий вызов QThread::wait() гарантирует как минимум еще одну итерацию event loop потока
wait ничего не гарантирует, даже более он сам блокирует поток в котором запущен до завершения ожидаемой нитки. В это время eventloop не крутиться.


Т.е Вы хотите сказать тем самым, что в книге Боровский А.Н. Qt 4.7+ Практическое программирование на С++ (2012г.) стр 169 Листинг 5.3 (глава 5) НЕ ВЕРЕН И В НЕМ ОШИБКА.
Записан
demaker
Птица говорун
*****
Offline Offline

Сообщений: 962


Просмотр профиля
« Ответ #25 : Декабрь 12, 2017, 18:03 »

Поправьте меня - возможно я не прав, но
возможно так лучше будет
Код
C++ (Qt)
thread->quit();
thread->wait();
delete thread;
 

И тогда вот этого не будет
Код:
QThread: Destroyed while thread is still running


Так тоже будет корректно, но тогда уж лучше сразу переменную на стеке делать и не мучаться с new/delete.



Вы имейти  ввиду переменную на стеке - т.е в run
Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #26 : Декабрь 12, 2017, 18:22 »

Wait не нужен если используется концепция с worker-ами, достаточно только quit(). Все там удалится как надо (если были коннекты к deleteLater() у воркета и у треда).
Записан

ArchLinux x86_64 / Win10 64 bit
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #27 : Декабрь 12, 2017, 18:24 »

то есть связка последовательных вызовов QThread::quit + QThread::wait не гарантирует итерации eventLoop'а?
Ну откройте уже исходники. Улыбающийся

wait блокирует выполнения нитки в котором он вызван, до завершения ожидаемой нитки.

Код
C++ (Qt)
bool QThread::wait(unsigned long time)
{
   Q_D(QThread);
   QMutexLocker locker(&d->mutex);
 
   if (d->thread_id == pthread_self()) {
       qWarning("QThread::wait: Thread tried to wait on itself");
       return false;
   }
 
   if (d->finished || !d->running)
       return true;
 
   while (d->running) {
       if (!d->thread_done.wait(locker.mutex(), time))
           return false;
   }
   return true;
}
 
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #28 : Декабрь 12, 2017, 18:26 »

Т.е Вы хотите сказать тем самым, что в книге Боровский А.Н. Qt 4.7+ Практическое программирование на С++ (2012г.) стр 169 Листинг 5.3 (глава 5) НЕ ВЕРЕН И В НЕМ ОШИБКА.
Честно говоря, я понятия не имею, что там написано. Улыбающийся
Легко может быть ошибка.
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 584


Просмотр профиля
« Ответ #29 : Декабрь 13, 2017, 08:20 »

Вы имейти  ввиду переменную на стеке - т.е в run

Я имею в виду такие костыли).

Код
C++ (Qt)
MyThread thread;
thread.moveToThread( &thread );
thread.start();
...
thread.quit();
thread.wait();
 

или даже такие

Код
C++ (Qt)
class MyThread : public QThread
{
public:
   MyThread ()
   {
       moveToThread( this );
   }
 
   ~MyThread ()
   {
       quit();
       wait();
   }
...
};
 
MyThread thread;
thread.start();
...
 


А по хорошему используйте QtConcurrent.

Wait не нужен если используется концепция с worker-ами, достаточно только quit(). Все там удалится как надо (если были коннекты к deleteLater() у воркета и у треда).

По-моему это уже обсуждали http://www.prog.org.ru/index.php?topic=31557.msg233459#msg233459 или что-то другое имеется в виду?
Записан
Страниц: 1 [2] 3   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.361 секунд. Запросов: 23.