Russian Qt Forum

Qt => Общие вопросы => Тема начата: Urvin от Ноябрь 27, 2008, 17:38



Название: [Qt 4.4] Поток и зависание
Отправлено: Urvin от Ноябрь 27, 2008, 17:38
Я хотел, чтобы у меня в программе по нажатию кнопки "старт" запускался хитрый процесс, который в зависимости от удачливости пользователя мог бы идти различное время.
И точно также я хотел, чтобы затянувшийся процесс пользователь мог остановить, нажав на кнопку "стоп"

Все работает отлично, окромя остановки потока - GUI замораживается и не дает на жать на кнопку остановки.

Подскажите, пожалуйста, как это исправить!

в функции run моего потока - только расчеты и вызов сиигналов (без различных таймеров и exec).


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: Пантер от Ноябрь 27, 2008, 18:02
Код в студию.


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: Urvin от Ноябрь 27, 2008, 18:11
markovthread.h
Код:
#ifndef MARKOVTHREAD_H
#define MARKOVTHREAD_H

#include <QThread>
#include <QString>
#include <QStringList>

class markovThread : public QThread {
    Q_OBJECT

    public:
        QString codeText;
        QString inputStr;

        markovThread();
        void run();

    signals:
        void messageGot(QString);
        void resultGot(QString);

    private:
        int checkSyntax(QString line);
        int interp(QString line, QString &str);
};


#endif // MARKOVTHREAD_H

markovthread.cpp
Код:
#include "markovthread.h"

markovThread::markovThread()
{
    codeText = "";
}

void markovThread::run()
{
    if (codeText=="") {
        emit messageGot("No code found");
        return;
    }
    if (inputStr=="") {
        emit messageGot("No input string specified");
        return;
    }

    codeText.replace("\r","");
    QStringList commands = codeText.split("\n", QString::SkipEmptyParts);

    int i, j;

    // Проверка синтаксиса каждой из строк
    bool allright=true;
    for (int i=0; i<commands.size(); i++)
    {
        j=checkSyntax(commands.at(i));

        if (j==2) {
            emit messageGot("Line " + QString("%1").arg(i+1) + ": No replace operator");
            allright=false;
        }
        else if (j==3)
        {
            emit messageGot("Line " + QString("%1").arg(i+1) + ": Two or more replace operators");
            allright=false;
        }
    }
    // Если имеются ошибки, дальшейшее исполнение невозможно
    if (!allright) {
        return;
    }


    // Непосредственное выполнение программы
    j=0; i=0;
    while (true)
    {
        j=interp(commands.at(i),inputStr);

        if (   j==0) {
            if (i<commands.size()-1)
            {
                i++;
            }
            else
            {
                emit messageGot("End with no finishing operator");
                break;
            }
        }
        else if(j==1)
        {
            emit resultGot(inputStr);
            i=0;
        }
        else if(j=2)
        {
            emit messageGot("End with finishing operator");
            break;
        }
    }
}



int markovThread::checkSyntax(QString line)
{
    // Если строка пустая, то ее синтаксис верено
    if (line=="")
    {
        return 1;
    }

    // Если строка начинается с обозначения комментария, ее синтаксис верен
    if (line.left(2)=="//")
    {
        return 1;
    }

    //Проверка расположения оператора замены
    // Если оператор в строке не найден - ошибка
    if (line.indexOf("->")==-1)
    {
        return 2;
    }
    // Если в строке более одного оператора замены, строка не верна
    if (line.lastIndexOf("->") != line.indexOf("->"))
    {
        return 3;
    }

    // Дальнейших проверок не требуется, строка синтаксически верна
    return 0;
}

// Функция интерпретирования языка.
// 0 - следующая строка
// 1 - программа заново
// 2 - остановка программы
int markovThread::interp(QString line, QString &str)
{
    // Находим пустую строку или комментарий, переходим на следующую строку
    if (line=="") {
        return 0;
    }
    if (line.left(2)=="//") {
        return 0;
    }


    QString s1,s2;
    int p=-1;

    // Поиск завершающего оператора
    p = line.indexOf("->.");
    if (p>-1)
    {
        s1=line.mid(0,p);
        s2=line.mid(p+3);

        // Первый операнд пустой/не пустой
        if (s1=="") {
            str = s2+str;
            return 2;
        }
        else {
            p = str.indexOf(s1);
            if (p>-1) {
                str.replace(s1,s2);
                return 2;
            }
            else {
                return 0;
            }
        }
    }

    // Поиск исполняемого оператора
    p = line.indexOf("->");
    if (p>-1)
    {
        s1=line.mid(0,p);
        s2=line.mid(p+2);

        if (s1=="")
        {
            str=s2+str;
            return 1;
        }
        else
        {
            p=str.indexOf(s1);
            if (p>-1)
            {
                str.replace(s1,s2);
                return 1;
            }
            else
            {
                return 0;
            }
        }
    }

    return 0;
}


Запуск и остановка:
Код:
void MainWindow::btnStart_Clicked()
{
    edtExec->setText("");
    lstMessages->clear();

    btnStart->setEnabled(false);
    btnStop->setEnabled(true);

    mThread->codeText="a->b";
    mThread->inputStr="aaabbb";
    mThread->start();
}

void MainWindow::btnStop_Clicked()
{
    mThread->quit();
}

если в старт поставить
mThread->codeText="b->b";
цикл станет бесконечным - это нормально, но надо иметь возможность остановить


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: SASA от Ноябрь 27, 2008, 18:26
Цитировать
void QThread::quit ()   [slot]

Tells the thread's event loop to exit with return code 0 (success). Equivalent to calling QThread::exit(0).

This function does nothing if the thread does not have an event loop.

Я бы написал
Код:
 mThread->terminate();
P.S. Настараживает 
Код:
else if(j=2)


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: Urvin от Ноябрь 27, 2008, 18:32
SASA, спасибо, исправил. Тем не менее - остается такая проблема, что при запуске бесконечного цикла GUI зависает и не отвечает на мои попытки нажать на кнопку остановки


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: ритт от Ноябрь 27, 2008, 18:46
пиши exec
без ивентлупа в данном случае ты только косяки заработаешь...


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: pastor от Ноябрь 27, 2008, 18:55
terminate() я бы не рекомендовал бы использовать. Это не правильное решение. Предлагаю следующее:

Код
C++ (Qt)
void markovThread::exitThread()
{
   if (!isRunning())
       return;
 
   m_stopMutex.lock();
   m_stopped = true;
   m_stopMutex.unlock();
 
   wait();
}
 
void markovThread::run()
{
   m_stopMutex.lock();
   m_stopped = false;
   m_stopMutex.unlock();
 
   ...
 
   while (!stopped)
   {
        ....
   }
}
 
void MainWindow::btnStop_Clicked()
{
   mThread->exitThread();
}

Вам нужно будет добавить 2 члена класса:

bool m_stopped;
QMutex m_stopMutex;


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: pastor от Ноябрь 27, 2008, 19:01
Тем не менее - остается такая проблема, что при запуске бесконечного цикла GUI зависает и не отвечает на мои попытки нажать на кнопку остановки

Где-то ошибка в логике работы программы. Нужно предусмотреть все случаи, чтобы небыло бесконечныйх циклов


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: Urvin от Ноябрь 27, 2008, 19:13
Случай бесконечного цикла, как ни странно обязан быть, и поэтому я выношу его в отдельный поток.
Но цикл надо останавливать.. Хотелось бы именно поточно, не изобретая прочих велосипедов


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: SASA от Ноябрь 29, 2008, 10:07
Про terminate() согласен с pastor.
Про вечный цикл. Можно в конце поставить msleep(1). Может поможет.
А как происходит connect c messageGot(QString) и resultGot(QString). Установлен ли там Qt::ConnectionType.

Где-то ошибка в логике работы программы. Нужно предусмотреть все случаи, чтобы небыло бесконечныйх циклов
У меня все потоки с вечными циклами - работают отлично.


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: Urvin от Ноябрь 29, 2008, 12:30
SASA, это в конструкторе MianWindow:
Код:
mThread = new markovThread();
    connect(mThread, SIGNAL(messageGot(QString)),this, SLOT(mThread_messageGot(QString)));
    connect(mThread, SIGNAL(resultGot(QString)),this, SLOT(mThread_resultGot(QString)));
    connect(mThread, SIGNAL(finished()),this, SLOT(mThread_finished()));


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: pastor от Ноябрь 29, 2008, 16:29
У меня все потоки с вечными циклами - работают отлично.

Я имел ввиду что нужно иметь условие выхода с такого цикла. Выше я привел такой код.


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: SASA от Декабрь 02, 2008, 17:02
SASA, это в конструкторе MianWindow:
Код:
mThread = new markovThread();
    connect(mThread, SIGNAL(messageGot(QString)),this, SLOT(mThread_messageGot(QString)));
    connect(mThread, SIGNAL(resultGot(QString)),this, SLOT(mThread_resultGot(QString)));
    connect(mThread, SIGNAL(finished()),this, SLOT(mThread_finished()));
Попробуй так
Код:
    connect(mThread, SIGNAL(messageGot(QString)),this, SLOT(mThread_messageGot(QString)), Qt::QueuedConnection);
    connect(mThread, SIGNAL(resultGot(QString)),this, SLOT(mThread_resultGot(QString)), Qt::QueuedConnection);
    connect(mThread, SIGNAL(finished()),this, SLOT(mThread_finished()), Qt::QueuedConnection);


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: Urvin от Декабрь 02, 2008, 23:11
все равно интерфейс виснет :-[


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: pastor от Декабрь 03, 2008, 11:42
все равно интерфейс виснет :-[

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

Код
C++ (Qt)
void markovThread::run()
{
   ...
 
   while (<>)
   {
        ....
        if (<>) {
            //do something
        } else if (<>) {
           // do something else
        } else {
           //do nothing
           msleep(10);
        }
 
   }
}

возможный код завершения цикла я привел выше.


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: SASA от Декабрь 03, 2008, 18:16
все равно интерфейс виснет :-[

А можешь выложиь демо проект.


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: Urvin от Декабрь 04, 2008, 10:56
Это то, что есть на данный момент: http://narod.ru/disk/4161918000/rivet_src.rar.html
Подключение потока к главной форме: mainwindow.cpp строки 235-238
Функции обработки данных потока: mainwindow.cpp строки с 362

mThread_messageGot - обработка соответсвующего события, вывод сообщения в элемент управления списком снизу
mThread_resultGot - вывод строки в правое текстовое поле
mThread_finished - окончание работы потока
btnStart_Clicked - нажатие кнопки запуска потока
btnStop_Clicked - нажатие кнопки остановки потока

Это программа для интерпретации марковских алгоритмов. Весь механизм упрятан в класс потока
Как работает:
1. В текстовое поле 'program' вводим
a->b
2. В мелкое текстовое поле 'execution' вводим
aabb
3. Нажимаем кнопку старта - справа от мелкого поля ввода
4. В поле результатов под кнопками получим bbbb - все верно

Если в поле программы вместо "a->b" ввести
a->b
b->a
программа уйдет в бесконечный цикл (причем это правильно!)  и в поле результатов последовательно будут выводиться строки bbbb и aaaa


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: Rcus от Декабрь 04, 2008, 12:47
Слишком много сигналов посылаете gui потоку => очередь событий не успевает очиститься => окно не обновляется (проверить можно вставив msleep(50) после emit resultGot(inputStr)).


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: Urvin от Декабрь 04, 2008, 13:07
Кнопка теперь нажимается)
а что теперь сделать чтобы поток все же останавливался по нажатию кнопки "стоп"?  ::)


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: Rcus от Декабрь 04, 2008, 15:17
/*sigh, vdiff*/
Код
C++ (Qt)
connect(btnStop, SIGNAL(clicked()), this, SLOT(btnStop_Clicked()));


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: pastor от Декабрь 04, 2008, 16:19
а что теперь сделать чтобы поток все же останавливался по нажатию кнопки "стоп"?  ::)

Вы наверное игнорируете мои посты? Я же предложил вариант. См выше


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: Urvin от Декабрь 04, 2008, 17:05
pastor, не игнорирую. он просто не останавливается(


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: pastor от Декабрь 04, 2008, 17:10
pastor, не игнорирую. он просто не останавливается(

неверю :) ибо сам такой код юзаю. Если нетрудно, выложи свой обновленный код (со sleep) я проверю.


Название: Re: [Qt 4.4] Поток и зависание
Отправлено: Urvin от Декабрь 04, 2008, 17:31
Все, исправился, работает  :D