Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: [РЕШЕНО] Работа таймеров в параллельном потоке
Форум на CrossPlatform.RU > Библиотеки > Qt > Qt Общие вопросы
AD
По каким-то причинам не работает таймер, который я хочу запустить в параллельном потоке.
В чем могут быть проблемы?
/// Класс потока - вращение вокруг осей заданное количество времени - длительное вращение
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
1. Обрати внимание, таймер создается в главном потоке!
2. В run() добавь exec().
AD
Цитата(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
AD, таймер так и остался в главном потоке.
Помести его в run().


П.С. А собственно будет ли влиять положение таймера, ведь ему не устанавливается родительский объект?
AD
Цитата(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
Цитата(AD @ 30.11.2009, 15:49) *
Почему в главном? Не понимаю:

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

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

Будет влиять на то, где будет крутиться таймер, точнее в чьем цикле обработки событий он будет проверяться и откуда будет испускаться сигнал.
Litkevich Yuriy
BRE, понял, ведь это следует из задачи :)
AD
Мда... Руки у меня, что-ли, кривые.... Не выходит.... :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
Проверка и испускание сигнала от таймера происходит в цикле обработки событий, который запускается методом exec.
А у тебя к этому времени таймер уже остановлен.
AD
Цитата(BRE @ 30.11.2009, 17:24) *
Проверка и испускание сигнала от таймера происходит в цикле обработки событий, который запускается методом exec.
А у тебя к этому времени таймер уже остановлен.

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

Да, и при этом таймер сам разрушиться.
AD
Блин. Я убрал строку остановки таймера, но в слот все-равно программа не заходит... Что еще может быть не так?
BRE
Попробуй такой код:
void ContinueRotation::run()
{
    QTimer timerReading;
    connect(&timerReading,  SIGNAL(timeout()), this, SLOT(reading()));
    timerReading.start(1000);
    exec();
}

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

Так.... попало. Интересно, а такой код приемлем?
/// Запуск потока вращения в обеих плоскостях длительный промежуток времени
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
Цитата(AD @ 30.11.2009, 20:27) *
А я правильно понимаю, что это таймер заканчивает свое действие, как только программа выходит из функции run()?
объект таймера будет разрушен по выходу, следовательно от таймера ничего не останется. Только вот метод run() никогда не закончится, а зациклится в методе exec(), также как и функция main()
int main (.....)
{
    QAplication a(...);
...
    a.exec();
}
методы exec() в потоке и в приложении делают примерно одно и тоже
BRE
Цитата(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
Ну... я уже ведь сказал, что запускается.... Проблема уже немного в другом: подружить мой цикл и вызов этого таймера... А вот как это сделать?
qDebug() выдает ошибку:
Цитата
error C2678: binary '<<' : no operator found which takes a left-hand operand of type 'QDebug' (or there is no acceptable conversion)

BRE
Цитата(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
Мда... Грустно, что нельзя это сделать. Задачка в следующем, которую никак не могу решить:
В этом потоке я отправляю на 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
Или делать такой изврат:
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
AD, ты уже в который раз спускаешся до подробностей прикладного кода, складывается такое впечатление, что ты хочешь чтобы за тебя программу написали.
AD
Цитата(Litkevich Yuriy @ 30.11.2009, 18:23) *
AD, ты уже в который раз спускаешся до подробностей прикладного кода, складывается такое впечатление, что ты хочешь чтобы за тебя программу написали.

Нет. Мне бы идею подсказать, как это сделать. Идея, а не код.
BRE
Что делает этот код?
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
Цитата(AD @ 30.11.2009, 21:24) *
Идея, а не код.
тогда зачем ты код выкладываешь?

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

Можно сказать, что да. Таймер, к сожалению, тут не подходит. Надо как-то по-другому выкручиваться. Можешь закрыть. А код выкладывал, для того, чтобы это не выглядело голословно, как раз таки!
BRE
Цитата(AD @ 30.11.2009, 18:38) *
Надо как-то по-другому выкручиваться.

Попробуй делать два таймера, по одном поворачивать, по другому читать.
И все будет Ok.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.