Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум на CrossPlatform.RU _ Qt Общие вопросы _ [РЕШЕНО] Работа таймеров в параллельном потоке

Автор: AD 30.11.2009, 12:48

По каким-то причинам не работает таймер, который я хочу запустить в параллельном потоке.
В чем могут быть проблемы?

/// Класс потока - вращение вокруг осей заданное количество времени - длительное вращение
class ContinueRotation: public QThread
{
    Q_OBJECT

private:
     QTimer _timerReading;            ///< таймер для запуска чтения из COM-порта

public:
     void startTimerReading() { _timerReading.start(1000); }
};


/// Запуск потока вращения в обеих плоскостях длительный промежуток времени
void ContinueRotation::run()
{
    connect(&_timerReading,  SIGNAL(timeout()), this, SLOT(reading()));
    ///
}

/// Запуск работы прожектора - ГЛАВНЫЙ поток
void ContinuousWork::startControl()
{
///
  _continue -> startTimerReading();
    _continue -> start();
}

Автор: BRE 30.11.2009, 12:51

1. Обрати внимание, таймер создается в главном потоке!
2. В run() добавь exec().

Автор: AD 30.11.2009, 15:31

Цитата(BRE @ 30.11.2009, 12:51) *
1. Обрати внимание, таймер создается в главном потоке!
2. В run() добавь exec().

Изменил. Не помогло. <_<

Code:
/// Запуск потока вращения в обеих плоскостях длительный промежуток времени
void ContinueRotation::run()
{
    connect(&_timerReading,  SIGNAL(timeout()), this, SLOT(reading()));
    _timerReading.start(1000);
///................
        exec();
}

Автор: Litkevich Yuriy 30.11.2009, 15:45

AD, таймер так и остался в главном потоке.
Помести его в run().


П.С. А собственно будет ли влиять положение таймера, ведь ему не устанавливается родительский объект?

Автор: AD 30.11.2009, 15:49

Цитата(Litkevich Yuriy @ 30.11.2009, 15:45) *
AD, таймер так и остался в главном потоке.
Помести его в run().

Почему в главном? Не понимаю:
/// Класс потока - вращение вокруг осей заданное количество времени - длительное вращение
class ContinueRotation: public QThread
{
    Q_OBJECT

private:
    QTimer _timerReading;                        ///< таймер для запуска чтения из COM-порта

private slots:
    void reading();
};

/// Запуск потока вращения в обеих плоскостях длительный промежуток времени
void ContinueRotation::run()
{
/// ................
    connect(&_timerReading,  SIGNAL(timeout()), this, SLOT(reading()));
    _timerReading.start(1000);

///      for(int timer_count=0; QTime::currentTime()<=finished_time;) { .... }

/// ..........

    exec();
}

Автор: BRE 30.11.2009, 16:11

Цитата(AD @ 30.11.2009, 15:49) *
Почему в главном? Не понимаю:

Потому что вызов конструктора объекта ContinueRotation выполняется в контексте главной нити (точнее той из которой он конструируется), а свой контекст нить получает, только при входе в метод run(). Либо перенеси создание таймера в run или попробуй делать ему moveToThread.

Цитата(Litkevich Yuriy @ 30.11.2009, 15:45) *
П.С. А собственно будет ли влиять положение таймера, ведь ему не устанавливается родительский объект?

Будет влиять на то, где будет крутиться таймер, точнее в чьем цикле обработки событий он будет проверяться и откуда будет испускаться сигнал.

Автор: Litkevich Yuriy 30.11.2009, 16:25

BRE, понял, ведь это следует из задачи :)

Автор: AD 30.11.2009, 17:02

Мда... Руки у меня, что-ли, кривые.... Не выходит.... :huh:

/// Запуск потока вращения в обеих плоскостях длительный промежуток времени
void ContinueRotation::run()
{
    connect(imitTsl, SIGNAL(continue_stop()), SLOT(exit()));
    connect(this, SIGNAL(working_light()), SLOT(workSpotlight()));

    QTimer timerReading;
    connect(&timerReading,  SIGNAL(timeout()), this, SLOT(reading()));
    timerReading.start(1000);

    for(int timer_count=0; QTime::currentTime()<=finished_time;)
    {
        if(_finished) break;
        timerTicks();
        if(timer_count == 60) timer_count = 0;
        if(!timer_count) emit working_light();
        ++timer_count;
    }

    emit work_ending();
    _is_time_read = false;
    timerReading.stop();
    exec();
}

Вот полная версия run(). Все-равно, не попадает в reading().

Автор: BRE 30.11.2009, 17:24

Проверка и испускание сигнала от таймера происходит в цикле обработки событий, который запускается методом exec.
А у тебя к этому времени таймер уже остановлен.

Автор: AD 30.11.2009, 17:27

Цитата(BRE @ 30.11.2009, 17:24) *
Проверка и испускание сигнала от таймера происходит в цикле обработки событий, который запускается методом exec.
А у тебя к этому времени таймер уже остановлен.

Хорошо, спасибо. А я правильно понимаю, что это таймер заканчивает свое действие, как только программа выходит из функции run()?

Автор: BRE 30.11.2009, 17:29

Цитата(AD @ 30.11.2009, 17:27) *
А я правильно понимаю, что это таймер заканчивает свое действие, как только программа выходит из функции run()?

Да, и при этом таймер сам разрушиться.

Автор: AD 30.11.2009, 17:38

Блин. Я убрал строку остановки таймера, но в слот все-равно программа не заходит... Что еще может быть не так?

Автор: BRE 30.11.2009, 17:46

Попробуй такой код:

void ContinueRotation::run()
{
    QTimer timerReading;
    connect(&timerReading,  SIGNAL(timeout()), this, SLOT(reading()));
    timerReading.start(1000);
    exec();
}


Автор: AD 30.11.2009, 18:01

Не попадает....

Так.... попало. Интересно, а такой код приемлем?

/// Запуск потока вращения в обеих плоскостях длительный промежуток времени
void ContinueRotation::run()
{
    connect(imitTsl, SIGNAL(continue_stop()), SLOT(exit()));
    connect(this, SIGNAL(working_light()), SLOT(workSpotlight()));

    QTimer timerReading;
    connect(&timerReading,  SIGNAL(timeout()), this, SLOT(reading()));
    timerReading.start(1000);
    exec();

    for(int timer_count=0; QTime::currentTime()<=finished_time;)
    {
        if(_finished) break;
        timerTicks();
        if(timer_count == 60) timer_count = 0;
        if(!timer_count) emit working_light();
        ++timer_count;
    }

    emit work_ending();
    _is_time_read = false;
}

Автор: Litkevich Yuriy 30.11.2009, 18:02

Цитата(AD @ 30.11.2009, 20:27) *
А я правильно понимаю, что это таймер заканчивает свое действие, как только программа выходит из функции run()?
объект таймера будет разрушен по выходу, следовательно от таймера ничего не останется. Только вот метод run() никогда не закончится, а зациклится в методе exec(), также как и функция main()
int main (.....)
{
    QAplication a(...);
...
    a.exec();
}
методы exec() в потоке и в приложении делают примерно одно и тоже

Автор: BRE 30.11.2009, 18:05

Цитата(AD @ 30.11.2009, 17:57) *
Не попадает....

В консоль что нибудь пишет?

добавь отладочный вывод:
void ContinueRotation::run()
{
    QTimer timerReading;
    qDebug() << connect(&timerReading,  SIGNAL(timeout()), this, SLOT(reading()));
    
    qDebug() << "Start timer"
    timerReading.start(1000);

    qDebug() << "Start eventloop"
    exec();
    qDebug() << "Stop eventloop"
}

Проверь, запускается ли нитка и т.д.


Цитата(AD @ 30.11.2009, 18:01) *
Так.... попало. Интересно, а такой код приемлем?

Нет.
Твой цикла будет запущен после выхода из метода exec и таймер уже сигналы отправлять не будет.

Ты должен выбрать, или использовать таймер с сигналами и запускать exec, или запускать свой цикл и не использовать таймер.

Автор: AD 30.11.2009, 18:07

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

Цитата
error C2678: binary '<<' : no operator found which takes a left-hand operand of type 'QDebug' (or there is no acceptable conversion)


Автор: BRE 30.11.2009, 18:09

Цитата(AD @ 30.11.2009, 18:07) *
Ну... я уже ведь сказал, что запускается....

А я уже ответ написал. ;)
Смотри предыдущий ответ.

Цитата(AD @ 30.11.2009, 18:07) *
qDebug() выдает ошибку:
Цитата
error C2678: binary '<<' : no operator found which takes a left-hand operand of type 'QDebug' (or there is no acceptable conversion)


#include <QDebug>

Автор: AD 30.11.2009, 18:18

Мда... Грустно, что нельзя это сделать. Задачка в следующем, которую никак не могу решить:
В этом потоке я отправляю на COM-порт команды, которые принимает прожектор. Ну команды расшифровываются так: повернуть на столько-то вверх, на столько-то вправо, на столько-то вниз, на столько-то влево. Одновременно мне нужно принимать от прожектора команды и анализировать. Ну что оно уже повернул на столько-то.

В своем цикле у меня делается следующее:

for(int timer_count=0; QTime::currentTime()<=finished_time;)
    {
        if(_finished) break;
        timerTicks();
        if(timer_count == 60) timer_count = 0;
        if(!timer_count) emit working_light();
        ++timer_count;
    }

Имеется счетчик времени. При определенном значении этого счетчика (раз в минуту) запускается функция выдачи команд, которая и занимается отправкой команд:
writing functions
/// Слот работы прожектора - вращения в обеих плоскостях
void ContinueRotation::workSpotlight()
{
    timerTicks();
    geted_angle_elev = -1, geted_azimuth = -1;

    /// Перемещение вниз и вправо до упора
    _angle_elevation = -110 * 10, _azimuth = 170 * 10;
    inf_to_ed_packet.low_angle_elev = LOWBYTE(_angle_elevation);
    inf_to_ed_packet.high_angle_elev = HIGHBYTE(_angle_elevation);
    inf_to_ed_packet.low_azim = LOWBYTE(_azimuth);
    inf_to_ed_packet.high_azim = HIGHBYTE(_azimuth);
    if(imitTsl) imitTsl -> writeFromCtrlContinuous();
    _is_time_read = true;
/// ВОТ ЗДЕСЬ МНЕ И НЕОБХОДИМО ПРОВЕРИТЬ НА СКОЛЬКО ОН УСПЕЛ ПОВЕРНУТЬСЯ.
    timerTicks();

    /// Перемещение вверх и влево до упора
    _angle_elevation = 10 * 10;        _azimuth = -170 * 10;
    inf_to_ed_packet.low_angle_elev = LOWBYTE(_angle_elevation);
    inf_to_ed_packet.high_angle_elev = HIGHBYTE(_angle_elevation);
    inf_to_ed_packet.low_azim = LOWBYTE(_azimuth);
    inf_to_ed_packet.high_azim = HIGHBYTE(_azimuth);
    if(imitTsl) imitTsl -> writeFromCtrlContinuous();
    _is_time_read = true;
/// ВОТ ЗДЕСЬ МНЕ И НЕОБХОДИМО ПРОВЕРИТЬ НА СКОЛЬКО ОН УСПЕЛ ПОВЕРНУТЬСЯ.
    timerTicks();

    /// Возврат в исходное положение
    _angle_elevation = 0;        _azimuth = 0;
    inf_to_ed_packet.low_angle_elev = LOWBYTE(_angle_elevation);
    inf_to_ed_packet.high_angle_elev = HIGHBYTE(_angle_elevation);
    inf_to_ed_packet.low_azim = LOWBYTE(_azimuth);
    inf_to_ed_packet.high_azim = HIGHBYTE(_azimuth);
    if(imitTsl) imitTsl -> writeFromCtrlContinuous();
    timerTicks();
}

Как видно из комментариев есть пара мест, где необходимо проверить значения, что я и пытался реализовать с помощью таймера.

Функции чтения выглядят так:
reading functions
/// Получение азимута
void ContinueRotation::readAzimuth(int& geted_azimuth, double limit)
{
    if(!imitTsl) return;
    QTime begining_time(QTime::currentTime());
    while(geted_azimuth < ((limit - 1.5) * 10) || geted_azimuth > ((limit + 1.5) * 10))
    {
        imitTsl -> readFromCtrlPage();
        geted_azimuth = VALFROMBYTES(inf_from_ed_packet.high_azim, inf_from_ed_packet.low_azim);
        int secs = begining_time.secsTo(QTime::currentTime());
        if(secs > 45) break;
        msleep(10);
    }
}

/// Получение угла места
void ContinueRotation::readAngleElevation(int& geted_angle_elev, double limit)
{
    if(!imitTsl) return;
    QTime begining_time(QTime::currentTime());
    while(geted_angle_elev < ((limit - 1.5) * 10) || geted_angle_elev > ((limit + 1.5) * 10))
    {
        imitTsl -> readFromCtrlPage();
        geted_angle_elev = VALFROMBYTES(inf_from_ed_packet.high_angle_elev, inf_from_ed_packet.low_angle_elev);
        int secs = begining_time.secsTo(QTime::currentTime());
        if(secs > 45) break;
        msleep(10);
    }
}

/// Слот чтения из COM-порта
void ContinueRotation::reading()
{
    if(!_is_time_read) return;

    readAngleElevation(geted_angle_elev, (double)_angle_elevation / 10.);
    readAzimuth(geted_azimuth, (double)_azimuth / 10.);
    _is_time_read = false;
}


Если у кого-то возникнут идеи, как это сделать, буду благодарен. Пока никаких других идей не возникает.... :(

Автор: BRE 30.11.2009, 18:19

Или делать такой изврат:

void ContinueRotation::run()
{
    connect(imitTsl, SIGNAL(continue_stop()), SLOT(exit()));
    connect(this, SIGNAL(working_light()), SLOT(workSpotlight()));

    QTimer timerReading;
    connect(&timerReading,  SIGNAL(timeout()), this, SLOT(reading()));
    timerReading.start(1000);

   QEventLoop loop;

    for(int timer_count=0; QTime::currentTime()<=finished_time;)
    {
        loop.processEvent();

        if(_finished) break;
        timerTicks();
        if(timer_count == 60) timer_count = 0;
        if(!timer_count) emit working_light();
        ++timer_count;
    }

    emit work_ending();
    _is_time_read = false;
}


Но я бы рекомендовал решить или согналы и exec, или свой цикл без сигналов.

Автор: Litkevich Yuriy 30.11.2009, 18:23

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

Автор: AD 30.11.2009, 18:24

Цитата(Litkevich Yuriy @ 30.11.2009, 18:23) *
AD, ты уже в который раз спускаешся до подробностей прикладного кода, складывается такое впечатление, что ты хочешь чтобы за тебя программу написали.

Нет. Мне бы идею подсказать, как это сделать. Идея, а не код.

Автор: BRE 30.11.2009, 18:29

Что делает этот код?

for(int timer_count=0; QTime::currentTime()<=finished_time;)
    {
        if(_finished) break;
        timerTicks();
        if(timer_count == 60) timer_count = 0;
        if(!timer_count) emit working_light();
        ++timer_count;
    }



Может проще запустить еще один таймер, который раз в секунду будет дергать working_light и проверять, если все повернулось останавливать таймер или всю нить.

Автор: Litkevich Yuriy 30.11.2009, 18:29

Цитата(AD @ 30.11.2009, 21:24) *
Идея, а не код.
тогда зачем ты код выкладываешь?

Для обсуждения идей создай отдельную тему. Проблема: "Работа таймера в параллельном потоке" решена?

Автор: AD 30.11.2009, 18:38

Цитата(Litkevich Yuriy @ 30.11.2009, 18:29) *
Для обсуждения идей создай отдельную тему. Проблема: "Работа таймера в параллельном потоке" решена?

Можно сказать, что да. Таймер, к сожалению, тут не подходит. Надо как-то по-другому выкручиваться. Можешь закрыть. А код выкладывал, для того, чтобы это не выглядело голословно, как раз таки!

Автор: BRE 30.11.2009, 18:43

Цитата(AD @ 30.11.2009, 18:38) *
Надо как-то по-другому выкручиваться.

Попробуй делать два таймера, по одном поворачивать, по другому читать.
И все будет Ok.

Форум Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)