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

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

Страниц: [1] 2 3   Вниз
  Печать  
Автор Тема: Как завершить поток таймера?  (Прочитано 17824 раз)
nvek
Гость
« : Август 24, 2017, 12:48 »

Опять я, опять, поток, опять таймер. Не понимаю.

Код:
#include "Timer.h"

наследник от QThread
Timer::Timer(int interval) :
m_interval(interval),
m_startFlag(true)
{

}

Timer::~Timer()
{
//delete timer;
//quit();
}

void Timer::run()
{
QTimer timer;
connect(&timer, &QTimer::timeout, this, &Timer::sg_TimOut);
connect(this, SIGNAL(timerStarted(int)), &timer, SLOT(start(int)));

if (m_startFlag)
timer.start(m_interval);
exec();

//mit finished(this->thread());
}

void Timer::startTimer(int interval)
{
m_interval = interval;
m_startFlag = true;
emit timerStarted(interval);
}

void Timer::stop()
{
m_startFlag = false;
emit timerStoped();
}

void Timer::quit()
{
//stop();
//QThread::quit();

}

как завершить этот поток?
QThread: Destroyed while thread is still running
Код:
m_tmr = new Timer(1000); //класс поток для таймера
    m_tmr->start();
connect(m_tmr, &Timer::sg_TimOut, this, &MainForm::updateByTimer, Qt::DirecConnection);
connect(m_tmr, &QThread::finished, m_tmr, &Timer::deleteLater);
так как же его правильно останавливать?
надо вначале остановить поток, затем удалить таймер.

Записан
nvek
Гость
« Ответ #1 : Август 24, 2017, 12:52 »

а что если это сделать по другому? может быть унаследовать его от обжекта и сделать просто movetothread()/
или попытаться использовать QtConcerent.
ведь метод который мене нужен чтобы выполнялся в потоке всего один.
Записан
demaker
Птица говорун
*****
Offline Offline

Сообщений: 960


Просмотр профиля
« Ответ #2 : Август 28, 2017, 23:17 »

А какая  задача стоит перед Вами?
Записан
Bepec
Гость
« Ответ #3 : Август 29, 2017, 01:59 »

У вас несколько ошибок.
Во первых вы не понимаете сигнал-слотовой системы и способов её соединения.
Во вторых вы пытаетесь отделить (зачем - непонятно) таймер от рабочего потока.
В третьих всё общение с потоками должно быть на сигнал-слотах, для облегчения ваших же проблем.

PS в некоторые моменты я сам задаюсь вопросом - как же будет работать поток в таком вот случае и иногда не нахожу ответов Улыбающийся
Записан
demaker
Птица говорун
*****
Offline Offline

Сообщений: 960


Просмотр профиля
« Ответ #4 : Август 29, 2017, 08:20 »

Думаю должно работать норм Строит глазки
*.h
Код
C++ (Qt)
class Thread : public QThread
{
   Q_OBJECT
 
public:
    Thread(QObject*parent = 0);
    virtual ~Thread();
 
public slots:
    void timeOut();
 
protected:
    void run();
}
 

*.cpp
Код
C++ (Qt)
Thread::Thread(QObject*parent): QThread(parent)
{
}
 
Thread::~Thread()
{
}
 
void Thread::run()
{
    QTimer timer;
    // Qt::DirectConnection чтобы слот обрабатывался в контексте того потока из которого был выслан сигнал
    connect(&timer,SIGNAL(timeout()),this,SLOT(timeOut()),Qt::DirectConnection);
    timer.start(50);
    exec();
}
 
void Thread::timeOut()
{
// do something
}
 

создаем и запускаем поток
Код
C++ (Qt)
void Widget::createThread()
{
   if(thread == NULL){
       thread = new Thread(0);
       thread->start();
   }
}
 

завершение потока
Код
C++ (Qt)
 
void Widget::quitThread()
{
    if(thread != NULL){
        thread->quit();
        thread->wait();
        delete thread; //thread->deleteLater();
        thread = NULL;
   }
}
 
 
« Последнее редактирование: Август 29, 2017, 08:39 от demaker » Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 574


Просмотр профиля
« Ответ #5 : Август 29, 2017, 13:58 »

Думаю должно работать норм Строит глазки
Код
C++ (Qt)
void Thread::run()
{
   QTimer timer;
   // Qt::DirectConnection чтобы слот обрабатывался в контексте того потока из которого был выслан сигнал
   connect(&timer,SIGNAL(timeout()),this,SLOT(timeOut()),Qt::DirectConnection);
   timer.start(50);
   exec();
}
 

Здесь кроется серьезная проблема. В такой реализации доступ к методу timeOut() экземпляра типа Thread производится из потока, который с ним никак не связан (в контексте Qt).
То есть априорно формируются грабли с одновременным многопоточным вызовом timeOut.
Записан
Bepec
Гость
« Ответ #6 : Август 29, 2017, 14:03 »

to ssoft: Ну, там надо в конструкторе moveToThread(this) дописать и тогда будет работать нормально Веселый

А так это извечная проблема потоков. Я кстати так до сих пор их и не осилил. К примеру, как правильно остановить событийный QThread ( где в run exec()).
Записан
demaker
Птица говорун
*****
Offline Offline

Сообщений: 960


Просмотр профиля
« Ответ #7 : Август 29, 2017, 15:12 »

to ssoft: Ну, там надо в конструкторе moveToThread(this) дописать и тогда будет работать нормально Веселый

А так это извечная проблема потоков. Я кстати так до сих пор их и не осилил. К примеру, как правильно остановить событийный QThread ( где в run exec()).
Смеющийся да согласен забыл написать Строит глазки
Код:
moveToThread(this)
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 574


Просмотр профиля
« Ответ #8 : Август 29, 2017, 15:42 »

to ssoft: Ну, там надо в конструкторе moveToThread(this) дописать и тогда будет работать нормально Веселый

Не будет). Если написать в конструкторе moveToThread(this), то либо проблема переносится в конструктор/деструктор, если экземпляр объекта создается в стеке; либо ваш экземпляр Thread зависнет в куче (memory leaks); либо придется писать какие-нибудь костыли (deleteLater() здесь не поможет).

А так это извечная проблема потоков. Я кстати так до сих пор их и не осилил. К примеру, как правильно остановить событийный QThread ( где в run exec()).

Да ничего в них проблемного нет). Все зависит от того, как нужно его остановить. Если хотите прервать - одно решение; если хотите завершить все текущие события в очереди - другое; если хотите завершить все события, пока очередь не опустеет (с учетом новых поступлений) - третье. По умолчанию Qt предоставляет метод завершения quit() со своей логикой.

Единственная особенность восприятия архитектуры Qt для работы с потоками связана с тем, что экземпляр типа QThread сам по себе организует только инфраструктуру для отдельного потока, но сам к нему не относится ни под каким соусом. То есть экземпляр типа QThread в рамках модели Qt связан с очередью того потока в котором он создан, а не с той, которую он сам организует. А метод moveToThread() - это еще тот костыль (причем встроенный в Qt), который следует использовать с большим количеством оговорок, ограничений и точным пониманием того, что вы делаете на свой страх и риск.
Записан
demaker
Птица говорун
*****
Offline Offline

Сообщений: 960


Просмотр профиля
« Ответ #9 : Август 29, 2017, 15:59 »

Ну тогда другой вариант.
Делать бесконечный цикл в run запускать счетчик и считать тики.
По дрстижению каког-то значения эмитить сигнал из потока.
Но опять же - будет ли это работать правильно и успевать отрабатывать.

Записан
Bepec
Гость
« Ответ #10 : Август 29, 2017, 16:00 »

to ssoft- работа будет происходить в одном потоке. И всё будет хорошо. Абсолютно.

А по поводу moveToThread, ну... такой себе костыль, реализация которого равнозначна реализации потоков Веселый
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 574


Просмотр профиля
« Ответ #11 : Август 30, 2017, 09:23 »

to ssoft- работа будет происходить в одном потоке. И всё будет хорошо. Абсолютно.

Предположим кто-то решил использовать такой класс

Пример 1. Размещение в стеке

Код
C++ (Qt)
void foo ()
{
   Thread thread;
   thread.moveToThread( &thread );
   thread.start();
}
 

Здесь реализуется непредсказуемое поведение. Как минимум получите предупреждение (QThread: Destroyed while thread is still running), как максимум крэш при обращении к атрибутам экземпляра thread.

Пример 2. Размещение в куче

Код
C++ (Qt)
void foo ()
{
   Thread * thread = new Thread;
   thread->moveToThread( thread );
   thread->start();
}
 

В этом случае удалять thread можно только после завершения потока, то есть, как минимум, нужно вызвать два метода quit и wait. Иначе возможны проблемы такие же как и в примере 1. Если не удалять thread, тогда буду утечки памяти (parent указать в этом случае нельзя). Нужно помнить, что к указателю thread теперь нельзя обращаться в этом потоке напрямую (или городить огород с мутексами и т.п.). Нюансов такого использования масса, я еще не все перечислил. Поэтому, если вы точно знаете что делаете, то флаг вам в руки. Но я как-то сомневаюсь, что новички это себе представляют.

По самой задаче: Как завершить поток таймера?

Если вы используете реализацию отсюда http://www.prog.org.ru/index.php?topic=31547.msg233225#msg233225 , то завершение потока таймера производится обычным способом завершения потока quit(), можно также подождать сам момент завершения с помощью wait().
Записан
Bepec
Гость
« Ответ #12 : Август 30, 2017, 13:34 »

Пример 1 - самая распространённая ошибка, вы убиваете объект после выхода их функции. Что блин, похоже на ошибку первоклашки в С++. И никоим образом не относится к потоку. Любой класс работающий дольше 5 секунд будет выдавать те же ошибки и поведение Веселый

Пример 2 - всё правильно. Поток можно удалять ТОЛЬКО после завершения работы.

Ответ = вызов в нём deleteLater и quit.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #13 : Август 30, 2017, 14:10 »

Есть 2 хорошо известных сущности: timer и thread. Так за каким же <template> называть Timer'ом наследника QThread Непонимающий Чтобы сразу капитально заморочить голову всем, и в первую очередь самому себе?

Чего Вы хотите добиться - я так и не понял. Запустить нитку в которой тикает таймер? QThread имеет методы startTimer и killTimer унаследованные от QObject, почему бы не использовать их? Как всегда. не забыть moveToThread(this)
Записан
ssoft
Программист
*****
Offline Offline

Сообщений: 574


Просмотр профиля
« Ответ #14 : Август 30, 2017, 16:08 »

Ответ = вызов в нём 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
« Последнее редактирование: Август 30, 2017, 16:13 от ssoft » Записан
Страниц: [1] 2 3   Вверх
  Печать  
 
Перейти в:  


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