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

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

Форум на CrossPlatform.RU _ Qt Общие вопросы _ Как запустить QThread чтобы он не вешал GUI?

Автор: flareguner 8.3.2010, 17:53

Я пробовал сделать так, как в примере в документации. Унаследовал QThread в классе и переопределил функцию void run(). Затем в другом приложении создал объект этого своего класса и запускаю его. GUI вешается и почти не отзывается. Большое спасибо тем, кто ответит.

Чуть не забыл: в классе наследующем поток мы ждем выполнения QProcess с waitForFinished( -1 ).

Автор: SABROG 8.3.2010, 20:01

Я конечно могу задавать наводящие вопросы, типа "где создается QProcess?", но быстрее будет, если ты выложишь исходники.

Автор: flareguner 8.3.2010, 21:17

Цитата(SABROG @ 8.3.2010, 20:01) *
Я конечно могу задавать наводящие вопросы, типа "где создается QProcess?", но быстрее будет, если ты выложишь исходники.


http://rghost.net/1123714


Из defines.h там только модифицированный дебаг.

В используем мы его так:

QMCToolProcessor *processor = new QMCToolProcessor;

processor->setArguments( argument_list );
processor->run();



Автор: DEADHUNT 8.3.2010, 21:48

QProcess::waitForStarted добавь и посмотри что от тебе вернёт.

Автор: SABROG 8.3.2010, 21:50

Не увидел ничего такого, что бы могло вешать GUI. Посмотри аналогичный http://www.lissyara.su/articles/freebsd/coding/qt4__++_programming Проверь работает ли он у тебя или сравни код. Qt в консоль никаких сообщений не пишет?

Автор: flareguner 8.3.2010, 21:55

Цитата(DEADHUNT @ 8.3.2010, 21:48) *
QProcess::waitForStarted добавь и посмотри что от тебе вернёт.


Подробнее, куда добавить? Может вы имели в виду waitForStarted для объекта process?

Цитата(SABROG @ 8.3.2010, 21:50) *
Не увидел ничего такого, что бы могло вешать GUI. Посмотри аналогичный http://www.lissyara.su/articles/freebsd/coding/qt4__++_programming Проверь работает ли он у тебя или сравни код. Qt в консоль никаких сообщений не пишет?



Посмотрю. Qt ничем не плюется.

Автор: DEADHUNT 8.3.2010, 21:57

Цитата(flareguner @ 8.3.2010, 21:55) *
Подробнее, куда добавить? Может вы имели в виду waitForStarted для объекта process?

да для process, прежде чем ждать его завершения, надо дождаться его запуска(и проверить, запускается ли он вообще).

Автор: flareguner 8.3.2010, 22:02

Цитата(SABROG @ 8.3.2010, 21:50) *
Не увидел ничего такого, что бы могло вешать GUI. Посмотри аналогичный http://www.lissyara.su/articles/freebsd/coding/qt4__++_programming Проверь работает ли он у тебя или сравни код. Qt в консоль никаких сообщений не пишет?



Мой код отличался тем, что небыло конструктора и деструктора. Их дописание ничего не изменило( Если это интересно, то в треде должен запускаться mencoder.

Автор: SABROG 8.3.2010, 22:04

Цитата(DEADHUNT @ 8.3.2010, 21:57) *
Цитата(flareguner @ 8.3.2010, 21:55) *
Подробнее, куда добавить? Может вы имели в виду waitForStarted для объекта process?

да для process, прежде чем ждать его завершения, надо дождаться его запуска(и проверить, запускается ли он вообще).


Не будет разницы никакой:

bool QProcess::waitForFinished(int msecs)
{
    Q_D(QProcess);
    if (d->processState == QProcess::NotRunning)
        return false;
    if (d->processState == QProcess::Starting) {
        QTime stopWatch;
        stopWatch.start();
        bool started = waitForStarted(msecs);
        if (!started)
            return false;
        if (msecs != -1)
            msecs -= stopWatch.elapsed();
    }

    return d->waitForFinished(msecs);
}


После вызова start() QProcess переходит в состояние Starting. Если даже это состояние поменялось на NotRunning в следтвии ошибки, то waitForFinished() об этом сразу увидит, если же мы всё еще в состоянии Starting, то он сам сам вызывает waitForStarted(). Ну и затем waitForFinished(), если всё удачно.

Цитата(flareguner @ 8.3.2010, 22:02) *
Мой код отличался тем, что небыло конструктора и деструктора. Их дописание ничего не изменило( Если это интересно, то в треде должен запускаться mencoder.


Проверь с какой-нибудь другой утилитой. Без QThread вообще код работал или всё написано с нуля без проверок?

Автор: flareguner 8.3.2010, 22:05

Цитата(DEADHUNT @ 8.3.2010, 21:57) *
Цитата(flareguner @ 8.3.2010, 21:55) *
Подробнее, куда добавить? Может вы имели в виду waitForStarted для объекта process?

да для process, прежде чем ждать его завершения, надо дождаться его запуска(и проверить, запускается ли он вообще).




Добавил его между start и waitForFinished. Возвращает истину.


Автор: BRE 8.3.2010, 22:07

Расскажи, что значит "вешается GUI"?

Для чего в конце метода run() стоит вызов exec()? Что он по твоему должен делать?

Код:

    int size_arguments = arguments.size() - 1;
    for(int i = 0; i <= size_arguments; i++)

наверное лучше изменить на:
    int size_arguments = arguments.size();
    for(int i = 0; i < size_arguments; i++)


Автор: SABROG 8.3.2010, 22:13

BRE прав, тебе нужно определиться, хочешь ли ты вообще читать вывод mencoder'a? Если да, то от всяких wait* и циклов нужно избавляться в пользу создания списка с очередью.

Автор: flareguner 8.3.2010, 22:20

Цитата(SABROG @ 8.3.2010, 22:04) *
Цитата(DEADHUNT @ 8.3.2010, 21:57) *
Цитата(flareguner @ 8.3.2010, 21:55) *
Подробнее, куда добавить? Может вы имели в виду waitForStarted для объекта process?

да для process, прежде чем ждать его завершения, надо дождаться его запуска(и проверить, запускается ли он вообще).


Не будет разницы никакой:

bool QProcess::waitForFinished(int msecs)
{
    Q_D(QProcess);
    if (d->processState == QProcess::NotRunning)
        return false;
    if (d->processState == QProcess::Starting) {
        QTime stopWatch;
        stopWatch.start();
        bool started = waitForStarted(msecs);
        if (!started)
            return false;
        if (msecs != -1)
            msecs -= stopWatch.elapsed();
    }

    return d->waitForFinished(msecs);
}


После вызова start() QProcess переходит в состояние Starting. Если даже это состояние поменялось на NotRunning в следтвии ошибки, то waitForFinished() об этом сразу увидит, если же мы всё еще в состоянии Starting, то он сам сам вызывает waitForStarted(). Ну и затем waitForFinished(), если всё удачно.

Цитата(flareguner @ 8.3.2010, 22:02) *
Мой код отличался тем, что небыло конструктора и деструктора. Их дописание ничего не изменило( Если это интересно, то в треде должен запускаться mencoder.


Проверь с какой-нибудь другой утилитой. Без QThread вообще код работал или всё написано с нуля без проверок?


Да всё работает. Дело суть просто в том, что классу скармливаются аргументы а он отдает их QProcess. Но с самого начала этот способ подвешивал GUI.





Цитата
Расскажи, что значит "вешается GUI"?


То окно, в котором мы запускаем этот поток никак ни на что не реагирует. виден только прогрессбар и он ползет. и все.
Цитата
    int size_arguments = arguments.size() - 1;
    for(int i = 0; i <= size_arguments; i++)

наверное лучше изменить на:
    int size_arguments = arguments.size();
    for(int i = 0; i < size_arguments; i++)



Согласен :)

Автор: SABROG 8.3.2010, 22:22

Цитата(flareguner @ 8.3.2010, 22:18) *
Но с самого начала этот способ подвешивал GUI.


Вообще это всё странно, так как QProcess::start() работает в асинхронном режиме, в теории никакие QThread'ы не нужны, так как QProcess должен работать по аналогии с QNetworkAccessManager или QFtp.

Автор: flareguner 8.3.2010, 22:24

Цитата(SABROG @ 8.3.2010, 22:22) *
Цитата(flareguner @ 8.3.2010, 22:18) *
Но с самого начала этот способ подвешивал GUI.


Вообще это всё странно, так как QProcess::start() работает в асинхронном режиме, в теории никакие QThread'ы не нужны, так как QProcess должен работать по аналогии с QNetworkAccessManager или QFtp.



Да, именно это и странно. Тем более, что даже в потоке он вешает гуй.

Автор: BRE 8.3.2010, 22:28

А покажи код, который запускает этот поток + как он соединяется со слотами. Короче всю обвязку.

Автор: SABROG 8.3.2010, 22:29

Проверь управление доходит до строчки идущей за waitForFinished(), если да, то скажи мне какая максимальная цифра в size_arguments.

Возможно это глупое предположение, но возможно программа слишком быстро запускается и завершается, настолько быстро, что долгий цикл:

    for(int i = 0; i < size_arguments; i++)
    {
        ...
    emit passChanged( i + 1 );
    }


Просто напросто "зафлуживает" GUI'шный поток сообщениями, на каждое из которых выполняется какая-нибудь не быстрая операция.

Автор: flareguner 8.3.2010, 22:34

Цитата(BRE @ 8.3.2010, 22:28) *
А покажи код, который запускает этот поток + как он соединяется со слотами. Короче всю обвязку.


http://rghost.net/1124334

Цитата(SABROG @ 8.3.2010, 22:29) *
Проверь управление доходит до строчки идущей за waitForFinished(), если да, то скажи мне какая максимальная цифра в size_arguments.

Возможно это глупое предположение, но возможно программа слишком быстро запускается и завершается, настолько быстро, что долгий цикл:

    for(int i = 0; i < size_arguments; i++)
    {
        ...
    emit passChanged( i + 1 );
    }


Просто напросто "зафлуживает" GUI'шный поток сообщениями, на каждое из которых выполняется какая-нибудь не быстрая операция.


Цикл завершается не быстро. Состоир из 1-2-3 итераций. Процесс конвертирования видео тоже не может проходить мгновенно. Я пробовал запускать такой же процесс вне треда и без ожидания завершения, и он не вешал GUI, хотя гуй парсил полностью вывод менкодера.

Автор: BRE 8.3.2010, 22:35

Так я и думал, новый поток запускается через start(). :)

Автор: SABROG 8.3.2010, 22:42

Цитата(BRE @ 8.3.2010, 22:35) *
Так я и думал, новый поток запускается через start(). :)


У QThread другого и нету :blink:

Если речь о новом процессе, то какая разница?

А у меня такая еще идея появилась. Дочерний процесс настолько сильно грузит процессор (все-таки ни что нибудь, а перекодирование!), что забирает все ресурсы и у родительского процесса. Что если запустить mencoder через утилиту nice, с пониженным приоритетом? Да еще и QThread setPriority() пониже выставить?

Автор: flareguner 8.3.2010, 22:44

Цитата(BRE @ 8.3.2010, 22:35) *
Так я и думал, новый поток запускается через start(). :)



А разве не run()-ом я его запускаю в файле video_converter.cpp? :rolleyes:

Автор: BRE 8.3.2010, 22:46

Цитата(SABROG @ 8.3.2010, 22:42) *
Если речь о новом процессе, то какая разница?

Камрад flareguner запускает новый поток вызовом метода run, т.е. новый поток не создается вообще.


Цитата(flareguner @ 8.3.2010, 22:44) *
А разве не run()-ом я его запускаю в файле video_converter.cpp? :rolleyes:

run это тело потока, а для того что бы он запустился в отдельном потоке, нужно его запускать через start().

Автор: flareguner 8.3.2010, 22:46

Цитата
Что если запустить mencoder через утилиту nice, с пониженным приоритетом? Да еще и QThread setPriority() пониже выставить?



Это тоже мысль. Сейчас опробую.

Автор: SABROG 8.3.2010, 22:51

Всё, нашел:

    processor->setArguments( stage2 );
    processor->run(); //!!!


flareguner, я ж просил все исходники выложить. Вторую ссылку не заметил :(

Цитата
Это тоже мысль. Сейчас опробую.

Да не поможет. Твоя проблема в waitForFinished(), он блокирует основной цикл событий, так как ты запустил поток через run(), а не через start().

Автор: flareguner 8.3.2010, 22:55

Цитата(BRE @ 8.3.2010, 22:46) *
Цитата(SABROG @ 8.3.2010, 22:42) *
Если речь о новом процессе, то какая разница?

Камрад flareguner запускает новый поток вызовом метода run, т.е. новый поток не создается вообще.


Цитата(flareguner @ 8.3.2010, 22:44) *
А разве не run()-ом я его запускаю в файле video_converter.cpp? :rolleyes:

run это тело потока, а для того что бы он запустился в отдельном потоке, нужно его запускать через start().



Вот это я облажался. Ну что же, больше всем вам спасибо что помогли и не поленились разгребать пионерский г-нокод. :clapping:

Автор: DEADHUNT 8.3.2010, 22:57

Цитата(SABROG @ 8.3.2010, 22:51) *
так как ты запустил поток через run(), а не через start().

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

Автор: SABROG 8.3.2010, 23:06

Цитата(DEADHUNT @ 8.3.2010, 22:57) *
это уже не запуск, а простой вызов метода. только тогда не понятно зачем QThread использовать.


Так и есть. Интересно почему тролли не поместили метод run() в protected секцию, тогда бы можно было бы его спокойно наследовать и при этом невозможно было бы его вызвать за пределами класса QThread и наследников, а только через start(). Брр, ничего не понимаю, он в protected секции. Почему-то его возможно вызвать :huh: ? Ой, кое-кто его вытащил "наружу":

class QMCToolProcessor :
    public QThread
{
    Q_OBJECT
    public:
    void setArguments ( const QStringList & );
    void setArguments ( const QString & );

    void run( );
    protected:
    QStringList arguments;
    QProcess *process;
    private slots:
    void hasStderr();
    void hasStdout();
    signals:
    void processHasStderr( QByteArray );
    void processHasStdout( QByteArray );



    void passChanged( int );
};


Вопрос к знатокам C++, возможно ли создать класс с protected членами, которым бы невозможно было бы поменять спецификатор доступа при наследовании?
Теоретически можно сделать run() чистой виртуальной функцией с private спецификатором доступа. Вот только наследование от QObject'a может не дать это сделать.

Автор: flareguner 9.3.2010, 0:01

Цитата(SABROG @ 8.3.2010, 23:06) *
Цитата(DEADHUNT @ 8.3.2010, 22:57) *
это уже не запуск, а простой вызов метода. только тогда не понятно зачем QThread использовать.


Так и есть. Интересно почему тролли не поместили метод run() в protected секцию, тогда бы можно было бы его спокойно наследовать и при этом невозможно было бы его вызвать за пределами класса QThread и наследников, а только через start(). Брр, ничего не понимаю, он в protected секции. Почему-то его возможно вызвать :huh: ? Ой, кое-кто его вытащил "наружу":

class QMCToolProcessor :
    public QThread
{
    Q_OBJECT
    public:
    void setArguments ( const QStringList & );
    void setArguments ( const QString & );

    void run( );
    protected:
    QStringList arguments;
    QProcess *process;
    private slots:
    void hasStderr();
    void hasStdout();
    signals:
    void processHasStderr( QByteArray );
    void processHasStdout( QByteArray );



    void passChanged( int );
};


Вопрос к знатокам C++, возможно ли создать класс с protected членами, которым бы невозможно было бы поменять спецификатор доступа при наследовании?
Теоретически можно сделать run() чистой виртуальной функцией с private спецификатором доступа. Вот только наследование от QObject'a может не дать это сделать.





вроде макрос Q_OBJECT делает что-то подобное.

Автор: SABROG 9.3.2010, 0:18

Цитата(flareguner @ 9.3.2010, 0:01) *
вроде макрос Q_OBJECT делает что-то подобное.


Если тролли поменяют объявление метода run() на такое, то возможно ошибок будет меньше:

private:
    virtual void run() = 0;

Автор: flareguner 9.3.2010, 0:45

Цитата(SABROG @ 9.3.2010, 0:18) *
Цитата(flareguner @ 9.3.2010, 0:01) *
вроде макрос Q_OBJECT делает что-то подобное.


Если тролли поменяют объявление метода run() на такое, то возможно ошибок будет меньше:

private:
    virtual void run() = 0;





Может я что-то путаю, но разве можно в дочернем классе переопределять private-элементы? Вроде бы только protected.

Автор: SABROG 9.3.2010, 0:56

Цитата(flareguner @ 9.3.2010, 0:45) *
Может я что-то путаю, но разве можно в дочернем классе переопределять private-элементы? Вроде бы только protected.


Я не пробовал, но вроде как это ограничение не распространяется на чистые виртуальные функции. К тому же это опять не решит нашей проблемы, спецификатор доступа у наследуемого класса для такой функции можно выбрать любой :(

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