Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Проблемы с многопотоковым приложением
Форум на CrossPlatform.RU > Библиотеки > Qt > Qt Общие вопросы
AD
Есть главное приложение, где выполняется отрисовка карты и траектории. А есть второстепенный поток, который читает файлы(логи) для отрисовки траектории. Как правильно передать управление из одной функции в другую? И вообще как правильнее решить этот вопрос. Вот примеры функций.
class LogReader
class LogReader: public QObject
{
    Q_OBJECT

protected:
        std::vector<Uint> m_vBlock;                ///< вектор прочитанных слов
        FILE* file;                                ///< файл загрузки (лог-файл)
    public:
        std::vector<ParamDescr*> rec_descr;        ///< вектор параметров из файла описания (файла загрузки)
        RThread* rthread;                        ///< указатель объект класса потока
        int count_files;                        ///< количество непрочитанных файлов
    protected:
        LOGRECORD ParseBlock();
                void ChangeDateTime(LOGRECORD& t, std::string compared_time, std::string compared_date);
                
                //.............
                std::vector<LOGRECORD> TRead();
                
};

/// Чтение файла загрузки
    vector<LOGRECORD> LogReader::TRead()
    {
        Uint var = (Uint)1E+6;
        bool bBlock = false;
        bool file_read = false;
        vector<LOGRECORD> log;
        time_t before_read;
        time(&before_read);
        while(fread(&var, sizeof(var), 1, file) > 0 && !feof(file))
        {
            Suint adr = var % 256;
            /// Определение начала/конца одной записи и записывание ее в вектор
            switch(adr)
            {
            case 0000:
                m_vBlock.clear();
                bBlock = true;
                break;
            case 0001:
                if(bBlock)
                {
                    LOGRECORD t = ParseBlock();
                    ChangeDateTime(t, "cur_time", "cur_date");
                    log.push_back(t);
                    file_read = true;
                    /// Сравнение временных меток
                    time_t contin_read;
                    time(&contin_read);
                    time_t delta = contin_read - before_read;
                    if(delta > 5)
                    {
                        rthread -> stop();
                        //rthread -> wait(1000);
                    }
                }
                bBlock = false;
                break;
            default:
                if(bBlock)
                    m_vBlock.push_back(var);
            }
        }

        /// Закрываем файл с логом
        ShutDown();
        /// Уменьшаем список непрочитанных файлов
        if(file_read)
        {
            --count_files;
            rthread -> stop();
        }

        return log;
    }

class RThread: public QThread
{
    Q_OBJECT

private:
    enum { MAXRECENTFILES = 7 };
    volatile bool stopped;            ///< переменная, контролирующая начало и конец выполнения потока
    QMutex mutex;                    ///< мьютекс для блокировки данных при работе с ними дополнительного потока

public:
    Reader::LogReader* logger;        ///< объект для чтения логов
    std::vector<LOGRECORD> read_log;///< вектор LOGRECORD , в котором находятся значения из считанного файла

protected:
    void run();

public:
    RThread(): stopped(false) {}
    void stop();
};

/// Запуск дополницельного потока
void RThread::run()
{
    forever
    {
        mutex.lock();
        if(stopped)
        {
            stopped = false;
            mutex.unlock();
            break;
        }
        read_log = logger -> TRead();
        mutex.unlock();
    }
    //QThread::run();
}

/// Остановка дополнительных потоков
void RThread::stop()
{
    mutex.lock();
    stopped = true;
    mutex.unlock();
}


Могу пояснить вопрос. Как вот в этом месте
if(delta > 5)
{
    rthread -> stop();
    //rthread -> wait(1000);
}

Перейти в функцию run()?
ViGOur
Если я тебя правильно понял, то rthread->start();

Но ИМХО лучше не в run передовать, а в какую-нибудь другую функцию данного потока посредством сигналов и слотов.
AD
Цитата(ViGOur @ 4.2.2008, 20:06) *
Если я тебя правильно понял, то rthread->start();

Но ИМХО лучше не в run передовать, а в какую-нибудь другую функцию данного потока посредством сигналов и слотов.

ну да, правильно поняли. Как с помощью сигналов и слотов, если несложно, помогите, пожалуйста!!!
ViGOur
Ну с сигналами и слотами все достаточно просто.

Создаем сигнал (threadSignal) и слот (threadSlot) и связываем их:
connect( this, SIGNAL( threadSignal), pObject, SLOT( threadSlot));

Разумеется их нужно обьявить в классе с ключевыми словами signals и slots. Для сигнала в moc файле быдет создано определение автоматически, для слота нужно будет писать самому, в принципе слот это обычная функция.

Затем, чтобы вызвать сигнал нужно написать:
emit threadSignal();

Еще нужно помнить, что слот threadSlot, будет выполнять в том потоке, в котором был создан pObject.
Вот в принципе и все.

Для лучшего понимая сигналов и слотов рекомендую прочитать: Сигналы и слоты.
AD
Цитата(ViGOur @ 4.2.2008, 20:32) *
Ну с сигналами и слотами все достаточно просто.

Создаем сигнал (threadSignal) и слот (threadSlot) и связываем их:
connect( this, SIGNAL( threadSignal), pObject, SLOT( threadSlot));

Разумеется их нужно обьявить в классе с ключевыми словами signals и slots. Для сигнала в moc файле быдет создано определение автоматически, для слота нужно будет писать самому, в принципе слот это обычная функция.

Затем, чтобы вызвать сигнал нужно написать:
emit threadSignal();

Еще нужно помнить, что слот threadSlot, будет выполнять в том потоке, в котором был создан pObject.
Вот в принципе и все.

Для лучшего понимая сигналов и слотов рекомендую прочитать: Сигналы и слоты.

В принципе понятно, но а в каком классе, из приведенных выше, надо сделать connect, а в каких эти threadSlot, threadSignal? Что за pObject?
ViGOur
Думаю, что в твоем варианте примерно так:
class LogReader: public QObject
{
   // ...
   LogReader();

protected signals:
   void threadSignal();

   // ...
};

LogReader::LogReader()
{
   // ...
   connect( this, SIGNAL( threadSignal), rthread, SLOT( threadSlot)); // разумеется rthread должен быть валидным.
}

class RThread: public QThread
{
   // ...

public slots:
   void threadSlot();

   // ...
};

void RThread::threadSlot()
{
   // Здесь что-то делается...
}
Ну и вызываешь emit threadSignal(); в том участке коде, что показывал в первом посте...
AD
Спасибо за помощь! :)
AD
АААА. Еще не все.
Сделал.
Выдает ошибку -
Error: Signals cannot have access specifier

Как избавиться. Нужно ли тело для threadSignal()?
ViGOur
Определение для сигнала не нужно, оно генерируется автоматически moc'ом.

В какой строчке выдает ошибку?
AD
Цитата(ViGOur @ 5.2.2008, 11:40) *
Определение для сигнала не нужно, оно генерируется автоматически moc'ом.

В какой строчке выдает ошибку?

На строчки
protected signals:
        void threadSignal();

Естественно, есть макрос Q_OBJECT.
Litkevich Yuriy
Цитата(AD22 @ 5.2.2008, 14:35) *
Нужно ли тело для threadSignal()?

Из книжки Макса Шлее:
Цитата
Сигналы определяются в классе, как и обычные методы, только без реализации. С точки зрения программиста они являются только протатипами методов, содержащихся в заголовочном файле определения класса


к стати у Шлее написано еще так:
Цитата
...не имеет смысла определять сигналы как protected, private, public
AD
Цитата(Litkevich Yuriy @ 5.2.2008, 11:47) *
Из книжки Макса Шлее:
Цитата
Сигналы определяются в классе, как и обычные методы, только без реализации. С точки зрения программиста они являются только протатипами методов, содержащихся в заголовочном файле определения класса


Спасибо. Я использую книжку
Жасмин Бланшет, Марк Саммерфилд
"Qt 4: Программирование GUI на C++"
Litkevich Yuriy
AD22, ты где сигнал посылаешь (emit threadSignal()) можешь пример привести?
AD
Цитата(Litkevich Yuriy @ 5.2.2008, 12:01) *
AD22, ты где сигнал посылаешь (emit threadSignal()) можешь пример привести?

Конечно! :)
vector<LOGRECORD> LogReader::TRead()
    {
        //......................
        while(fread(&var, sizeof(var), 1, file) > 0 && !feof(file))
        {
            Suint adr = var % 256;
            /// Определение начала/конца одной записи и записывание ее в вектор
            switch(adr)
            {
            case 0000:
                m_vBlock.clear();
                bBlock = true;
                break;
            case 0001:
                if(bBlock)
                {
                    LOGRECORD t = ParseBlock();
                    ChangeDateTime(t, "cur_time", "cur_date");
                    log.push_back(t);
                    file_read = true;
                    /// Сравнение временных меток
                    time_t contin_read;
                    time(&contin_read);
                    time_t delta = contin_read - before_read;
                    if(delta > 5)
                        emit threadSignal();
                }
                bBlock = false;
                break;
            default:
                if(bBlock)
                    m_vBlock.push_back(var);
            }
        }

        //..................
        return log;
    }


На счет protected, private, public - да, в этом была ошибка - убрал!
AD
Теперь ошибка при выполнении. Ломается программа на строчках:
LogReader::LogReader(std::string FileName)
    {
        fopen_s(&file, FileName.c_str(), "rb");
        connect(this, SIGNAL(threadSignal()), rthread, SLOT(threadSlot()));
    }
Litkevich Yuriy
тема такая, высылать сигнал может только тот класс в котором сигнал определен, я так понял что он у тебя в другом классе, тогда сделай так:
class LogReader: public QObject
{
   // ...
   LogReader();

protected signals:
   void threadSignal();

public:
   void sendMySignal()
   {
     emit threadSignal();
    }

   // ...
};


а в коде где ты делал emit threadSignal(); вызывай метод sendMySignal()
AD
Цитата(Litkevich Yuriy @ 5.2.2008, 12:12) *
тема такая, высылать сигнал может только тот класс в котором сигнал определен, я так понял что он у тебя в другом классе, тогда сделай так:
class LogReader: public QObject
{
   // ...
   LogReader();

protected signals:
   void threadSignal();

public:
   void sendMySignal()
   {
     emit threadSignal();
    }

   // ...
};


а в коде где ты делал emit threadSignal(); вызывай метод sendMySignal()

Проблема была не в этом. Я в конструкторе делал connect, а в этот момент rthread был еще не инициализирован. Ошибку поправил, но все-равно траектория не выводится, хотя работает приложение быстро и без ошибок выполнения! :(
ViGOur
Цитата(AD22 @ 5.2.2008, 12:09) *
connect(this, SIGNAL(threadSignal()), rthread, SLOT(threadSlot()));
А rthread у тебя валидный? Думаю, что нет, потому и валится...

Цитата(Litkevich Yuriy @ 5.2.2008, 12:12) *
я так понял что он у тебя в другом классе
Если он сделал так как я показал, то в том классе, что нужно, в LogReader. :)
ViGOur
Цитата(AD22 @ 5.2.2008, 12:24) *
Ошибку поправил, но все-равно траектория не выводится, хотя работает приложение быстро и без ошибок выполнения! sad.gif
Если она у тебя должна выводиться в слоте, то поставь в нем точку останова и посмотри, правильно отрабатывает ли...
AD
Цитата(ViGOur @ 5.2.2008, 12:27) *
Если она у тебя должна выводиться в слоте, то поставь в нем точку останова и посмотри, правильно отрабатывает ли...

Да... она не отрабатывает. Я сделал так, как Вы и показывали. Спасибо за оперативную и продуктивную помощь. Буду разбираться с проблемами. Если что, могу вопросы задавать?
Litkevich Yuriy
на то он и форум ;)
ViGOur
Цитата(Litkevich Yuriy @ 5.2.2008, 12:40) *
на то он и форум wink.gif
Именно. :)
AD
Понял в чем проблема, но не знаю как выправить.
Смысл такой в функции LogReader::TRead() в месте вызова сигнала я хочу, чтобы в вектор read_log добавлялись считанные значения, а в слоте я опять же обращаюсь к функции TRead() для того, чтобы вернуть этот вектор. Как это сделать, подскажите, пожалуйста!
ViGOur
Цитата(AD22 @ 5.2.2008, 13:34) *
а в слоте я опять же обращаюсь к функции TRead() для того, чтобы вернуть этот вектор
Не понятно, зачем тебе из слота обращаться к функции TRead()?
Когда можно сделать так, заполняешь вектор read_log в LogReader::TRead(), после заполнения отправляешь сигнал, в слоте получаешь вектор read_log и читаешь его. :
AD
Цитата(ViGOur @ 5.2.2008, 14:38) *
Не понятно, зачем тебе из слота обращаться к функции TRead()?
Когда можно сделать так, заполняешь вектор read_log в LogReader::TRead(), после заполнения отправляешь сигнал, в слоте получаешь вектор read_log и читаешь его. :

Так ведь в слоте я и получаю вектор структур LOGRECORD!
ViGOur
приведи код, просто не совсем понятно, что ты хочешь. :)
AD
Цитата(ViGOur @ 5.2.2008, 15:56) *
приведи код, просто не совсем понятно, что ты хочешь. :)

Вот код, который сделан со всеми Вашими поправками.
/// Чтение файла загрузки
    vector<LOGRECORD> LogReader::TRead()
    {
        Uint var = (Uint)1E+6;
        bool bBlock = false;
        bool file_read = false;
        vector<LOGRECORD> log;
        time_t before_read;
        time(&before_read);
        while(fread(&var, sizeof(var), 1, file) > 0 && !feof(file))
        {
            Suint adr = var % 256;
            /// Определение начала/конца одной записи и записывание ее в вектор
            switch(adr)
            {
            case 0000:
                m_vBlock.clear();
                bBlock = true;
                break;
            case 0001:
                if(bBlock)
                {
                    LOGRECORD t = ParseBlock();
                    ChangeDateTime(t, "cur_time", "cur_date");
                    log.push_back(t);
                    file_read = true;
                    /// Сравнение временных меток
                    time_t contin_read;
                    time(&contin_read);
                    time_t delta = contin_read - before_read;
                    /*if(delta > 5)
                        emit threadSignal();*/
                }
                bBlock = false;
                break;
            default:
                if(bBlock)
                    m_vBlock.push_back(var);
            }
        }

/// Запуск дополницельного потока
void RThread::run()
{
    forever
    {
        mutex.lock();
        if(stopped)
        {
            stopped = false;
            mutex.unlock();
            break;
        }
        mutex.unlock();
        read_log = logger -> TRead();
    }
}

/// Остановка дополнительных потоков
void RThread::stop()
{
    mutex.lock();
    stopped = true;
    mutex.unlock();
}

/// Слот, в который попадаем в момент чтения лог-файла
void RThread::threadSlot()
{
    mutex.lock();
    read_log = logger -> TRead();
    mutex.unlock();
}

В этом варианте все-равно не работает.
А мне бы хотелось, чтобы в момент
time_t contin_read;
time(&contin_read);
time_t delta = contin_read - before_read;
if(delta > 5)
        emit threadSignal();

передавались считанные данные в read_log!
ViGOur
А добавить RThread::run работает?
void RThread::run()
{
   // ...

  exec();
}
Насчет остального не могу сейчас сказать, так как голова совсем уже не соображает. :)
AD
Нет не работает. В смысле, не выводится траектория!!! Буду благодарен за любой дельный совет. :)
ViGOur
Слушай можешь выложить исходники с файлом который ты читаешь в архиве, я полностью гляну, что у тебя и как там?
А то вопросов слишком много... :)

И в кратце обьясни, что есть, что нужно.
AD
Цитата(ViGOur @ 6.2.2008, 9:56) *
Слушай можешь выложить исходники с файлом который ты читаешь в архиве, я полностью гляну, что у тебя и как там?
А то вопросов слишком много... :)

И в кратце обьясни, что есть, что нужно.

Вопросов много - это ведь хорошо :) Вопросы на одну проблему.
Есть почти все. Надо, чтобы выводилась траектория. Т.е. правильно заполнялись вектора read_log и log - из которых берется информация для вывода траектории.
AD
Вот файлы. При выполнении предлагается выбрать вначале ини файл. Затем лог-файл. Ини-файл - начальные данные, Лог-файл - данные, полученные с прибора.


P.S. ини файл надо выбирать TTVPlane.ini
AD
Проблема вроде бы решена. ВСЕМ СПАСИБО! Если будут другие проблемы - буду обращаться :)
ViGOur
Ну вот, а я только взялся за рассмотрение твоего кода... :)

Чтобы хоть?
AD
Цитата(ViGOur @ 6.2.2008, 13:24) *
Ну вот, а я только взялся за рассмотрение твоего кода... :)

Чтобы хоть?

Что чтобы хоть? А можно из поста удалить зип-файлы? Мне бы не хотелось, чтобы они там валялись. Заранее благодарю.
Admin
Цитата(AD22 @ 6.2.2008, 13:46) *
А можно из поста удалить зип-файлы? Мне бы не хотелось, чтобы они там валялись
Удалил...
AD
Цитата(Admin @ 6.2.2008, 14:55) *
Удалил...

Thank you! :)
ViGOur
Цитата(AD22 @ 6.2.2008, 13:46) *
Что чтобы хоть?
Очепятался, что было хоть? :)
AD
Цитата(ViGOur @ 6.2.2008, 16:26) *
Очепятался, что было хоть? :)

Да посоветовали, переделал основательно. Нет теперь 3 вектор одного типа. Остался только log! НУ еще много что переделал. Сделал без сигнал-слот. Теперь вызываю из дополнительного потока функцию главного потока.
AD
Вопрос на будущее. Мне пришлось "убивать"(delete) поток и создавать заново. А как его остановить?
terrible
void QThread::quit()
или
void QThread::exit ( int returnCode = 0 )
AD
Цитата(terrible @ 6.2.2008, 17:33) *
void QThread::quit()
или
void QThread::exit ( int returnCode = 0 )

Так их переопределять надо? Или просто вызвать?
ViGOur
просто вызвать
AD
У меня есть огромная просьба: проблема, которая обсуждалась в этой теме уже давно решена. Но решена не совсем методами Qt. Если у кого-то есть хороший, яркий, понятный пример использования мьютексов и дополнительных потоков, пожалуйста, выложите здесь!!! Примеры, которые приведены в книжке Бланшетта на эту тему чересчур просты! Просьба пример привести посложнее, чем в книге и яркий!

P.S. Просто нашел в программе места, где было бы удобно использовать доп. поток, а внятного применения приведенных в книге примеров найти не смог!
ViGOur
Ты просто по всей видимости не доконца понимаешь как работаю процессы и потоки.
Почитай книжку Джеффри Рихтера Windows для профессионалов, в ней хорошо описана работа с процессами, потоками и их синхронизация, хоть и на WinApi, но достаточно хорошо разжеваны все аспекты... :)
AD
Цитата(ViGOur @ 28.8.2008, 14:48) *
Ты просто по всей видимости не доконца понимаешь как работаю процессы и потоки.
Почитай книжку Джеффри Рихтера Windows для профессионалов, в ней хорошо описана работа с процессами, потоками и их синхронизация, хоть и на WinApi, но достаточно хорошо разжеваны все аспекты... :)

Ты про теоретическую основу? :) Целый предмет был - параллельное программирование. Проходили кроссплатформенные MPI and OpenMP! Книжка эта есть в электронном виде, спасибо за совет, кое-что перечитаю... :) Мне просто примеры более яркие посмотреть, теорию знаю - а вот практики маловато.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2024 IPS, Inc.