Qt::QThread
От: hotdox  
Дата: 13.05.09 05:45
Оценка:
Пишу GUI приложение на Qt
Что хочется:
[list=1] GUI поток
[list=2] Background поток
Механизм ообщения:
Background поток висит все время получает запросы на работу, отправляет результаты работы и лог-сообщения, общение происходит через slots/signals
Что сделал:
class CBackgroundStorage::QThread с пустым методом run() и отписал слоты
int main(){
CBakgroundStorage storage;
MainWindow w;
QObject::connect(w ,...,stoarge,..., Qt::QueuedConnection);
QObject::connect(stoarge,...,w, Qt::QueuedConnection);
w.show();
storage.start();
}

Что имею:
Первым же делом CBackGroundStorage принимается парсить файлы (очень длительный процесс), но GUI при этом подвисает и лог сообщения не показывает — я так понимаю, что никакой многопоточности фактически нет

Вопрос — что делать, чтоб многопоточность появилась? У меня такое подозрение, что что-то в корне не так. Если не понятно то буду писать минимумпример




21.01.10 13:02: Перенесено модератором из 'C/C++. Прикладные вопросы' — Кодт
Re: Qt::QThread
От: K13 http://akvis.com
Дата: 13.05.09 08:48
Оценка:
H>Вопрос — что делать, чтоб многопоточность появилась? У меня такое подозрение, что что-то в корне не так. Если не понятно то буду писать минимумпример

Наверное стоит. потому что приведенный фрагмент нежизнеспособен -- как минимум отсутствует QApplication.
Re: Qt::QThread
От: Denys V. Украина http://ua.linkedin.com/in/dvalchuk
Дата: 13.05.09 09:07
Оценка:
Здравствуйте, hotdox, Вы писали:

H>Вопрос — что делать, чтоб многопоточность появилась? У меня такое подозрение, что что-то в корне не так. Если не понятно то буду писать минимумпример


советую посмотреть примеры... почитать документацию по Qt...
С уважением Denys Valchuk

IMHO чем больше мнений тем оптимальней выбор варианта... :)
Re[2]: Qt::QThread
От: hotdox  
Дата: 13.05.09 09:37
Оценка:
Здравствуйте, Denys V., Вы писали:

DV>советую посмотреть примеры... почитать документацию по Qt...


Даже книги читал, там не описаны подобные случаи
Сделал минимумпример весь проект — http://rghost.ru/239203

//=========================
// mainwindow.h
//
//=========================
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtGui/QMainWindow>
#include <QTextEdit>
#include <QPushButton>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
signals:
    void start();
public slots:
    void getLog(QString message);
private slots:
    void getButton();
private:
    QTextEdit   *log;
    QPushButton *btn;
};

#endif // MAINWINDOW_H

//=========================
//mainwindow.cpp
//
//=========================
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QGroupBox>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent){

    QVBoxLayout *box    =   new QVBoxLayout;

    btn =   new QPushButton(this);
    log =   new QTextEdit(this);

    box->addWidget(btn);
    box->addWidget(log);

    QGroupBox   *all_group  =   new QGroupBox(this);
    all_group->setLayout(box);
    setCentralWidget(all_group);

    connect(btn,SIGNAL(clicked()),SLOT(getButton()));
}

MainWindow::~MainWindow(){

}

void MainWindow::getLog(QString message){
    log->append(message);
}
void MainWindow::getButton(){
    emit start();
}
//=========================
//backgroundworker.h
//
//=========================
#ifndef BACKGROUNDWORKER_H
#define BACKGROUNDWORKER_H

#include <QThread>

class CBackgroundWorker : public QThread{
    Q_OBJECT

public:
    CBackgroundWorker();
    void run();
signals:
    void Log(QString message);
public slots:
    void work();
private:
    bool is_work;
};

#endif // BACKGROUNDWORKER_H


//=========================
//backgroundworker.cpp
//
//=========================
#include "backgroundworker.h"

CBackgroundWorker::CBackgroundWorker()
:is_work(false){
}
void CBackgroundWorker::run(){
}
void CBackgroundWorker::work(){

    size_t res;
    if(is_work)
        return;

    is_work =   true;


    for(int i = 0; i != 1000; ++i){
        for(int j = 0; j != 1000000; ++j){
            res =   i*j;
        }
        emit Log("another thousand completed");
    }

    is_work =   false;
}

//=========================
//main.cpp
//
//=========================
#include <QtGui/QApplication>
#include "mainwindow.h"
#include "backgroundworker.h"

int main(int argc, char *argv[]){

    QApplication a(argc, argv);
    MainWindow          w;
    CBackgroundWorker   worker;

    QObject::connect(&w, SIGNAL(start()), &worker, SLOT(work()));
    QObject::connect(&worker, SIGNAL(Log(QString)), &w, SLOT(getLog(QString)));

    worker.start();
    w.show();


    return a.exec();
}
Re[3]: Qt::QThread
От: little_alex  
Дата: 13.05.09 10:02
Оценка:
Здравствуйте, hotdox, Вы писали:

H>Здравствуйте, Denys V., Вы писали:


DV>>советую посмотреть примеры... почитать документацию по Qt...


H>Даже книги читал, там не описаны подобные случаи

H>Сделал минимумпример весь проект — http://rghost.ru/239203

Экземпляр QThread "находится" в потоке-родителе (переменная находится в том потоке, где создана). Соответственно все твои слоты в главном потоке вызываются. А новый созданный поток "живет" только пока QThread::run выполняется — в твоем случае очень не долго. Ну и is_run переменная — источник проблем: присваивание is_run=false за счет переупорядочивания инструкций процессором и компилятором может произойти до того, как расчет завершится.
Re[4]: Qt::QThread
От: hotdox  
Дата: 13.05.09 15:06
Оценка:
Спасибо за ответ

Здравствуйте, little_alex, Вы писали:

_>Экземпляр QThread "находится" в потоке-родителе (переменная находится в том потоке, где создана). Соответственно все твои слоты в главном потоке вызываются. А новый созданный поток "живет" только пока QThread::run выполняется — в твоем случае очень не долго.

Меня смущал пример с таймером и полоской прогресса, но там таймер создается в run() и его сигналы вызываются не из главного потока.

_>Ну и is_run переменная — источник проблем: присваивание is_run=false за счет переупорядочивания инструкций процессором и компилятором может произойти до того, как расчет завершится.

Это такой самодельный мьютекс, не обращайте внимания
Re[3]: Qt::QThread
От: bkat  
Дата: 13.05.09 15:11
Оценка: 2 (1)
Здравствуйте, hotdox, Вы писали:

H>Здравствуйте, Denys V., Вы писали:


DV>>советую посмотреть примеры... почитать документацию по Qt...


H>Даже книги читал, там не описаны подобные случаи

H>Сделал минимумпример весь проект — http://rghost.ru/239203

Что бросилось в глаза:
1) В конструкторе потока надо вызвать moveToThread(this);

CBackgroundWorker::CBackgroundWorker()
:is_work(false)
{
  moveToThread(this);
}

Иначе все signal/slot соединения будут работать в контексте потока,
где ты создал свой объект (в твоем случае — главный поток)

2) метод run у тебя пустой. Это означает, что поток запускается и сразу останавливается.
В методе run надо вызвать метод exec, чтобы запустить цикл обработки сообщений потока.

То, что у тебя, запускает поток, который тут же завершается, и вся обработка происходит в главном потоке.
Re[4]: Qt::QThread
От: hotdox  
Дата: 13.05.09 17:06
Оценка:
Здравствуйте, bkat, Вы писали:

B>1) В конструкторе потока надо вызвать moveToThread(this);

B>В методе run надо вызвать метод exec, чтобы запустить цикл обработки сообщений потока.

Внес исправления, и все заработало как надо, большое спасибо!

Однако еще вопрос: что делать если поток занят работой, а приложение закрыли — появляется ошибка и предлагается сохранить bugreport
попробовал разные варианты
QObject::connect(&w, SIGNAL(destroyed()), &storage, SLOT(terminate()));
QObject::connect(&w, SIGNAL(destroyed()), &storage, SLOT(quit()));

Однако ничего не помогло
Re[5]: Qt::QThread
От: bkat  
Дата: 13.05.09 17:58
Оценка:
Здравствуйте, hotdox, Вы писали:

H>Здравствуйте, bkat, Вы писали:


B>>1) В конструкторе потока надо вызвать moveToThread(this);

B>>В методе run надо вызвать метод exec, чтобы запустить цикл обработки сообщений потока.

H>Внес исправления, и все заработало как надо, большое спасибо!


H>Однако еще вопрос: что делать если поток занят работой, а приложение закрыли — появляется ошибка и предлагается сохранить bugreport


Ожидаемый вопрос
У тебя скорей всего поток все еще работает, после того, как объект CBackgroundWorker уже разрушен.
По хорошему, тебе надо попросить поток завершить полезную работу (именно попросить, а не убить поток),
потом завершить цикл обратки сообщений потока и завершить сам поток.
Главный поток должен ждать пока поток не завершиться и только после этого можно
уничтожать объект CBackgroundWorker.

Способов это сделать уйма.
Попробуй, к примеру так (твой код с дополнениями):

int main(int argc, char *argv[]){

    QApplication a(argc, argv);
    MainWindow          w;
    CBackgroundWorker   worker;

    QObject::connect(&w, SIGNAL(start()), &worker, SLOT(work()));
    QObject::connect(&worker, SIGNAL(Log(QString)), &w, SLOT(getLog(QString)));

    worker.start();
    w.show();

    int rc = a.exec();

    worker.quit(); // просим завершить цикл обработки сообщений потока
    worker.wait(); // ждем пока поток не завершит свою работу
    return rc;
}


Одна проблема тут точно есть.
До тех пор пока CBackgroundWorker::work() что-то делает, цикл обработки сообщений потока не завершится
и тем самым приложение будет висеть, пока CBackgroundWorker::work() не доделает то,
что все равно никому не нужно (приложение ведь закрывается).
Если это проблема, то тебе надо будет подумать, как в CBackgroundWorker::work() передать твое
пожелание завершить работу как можно скорее.
Банального bool флага должно хватить за глаза.
Re: Qt::QThread
От: Blade Runner Земля  
Дата: 15.05.09 15:00
Оценка:
Здравствуйте, hotdox, Вы писали:

H>Пишу GUI приложение на Qt

H>Что хочется:
H> [list=1] GUI поток
H> [list=2] Background поток
H> Механизм ообщения:
H> Background поток висит все время получает запросы на работу, отправляет результаты работы и лог-сообщения, общение происходит через slots/signals
H>Что сделал:
H> class CBackgroundStorage::QThread с пустым методом run() и отписал слоты
H>
H>int main(){
H>CBakgroundStorage storage;
H>MainWindow w;
H>QObject::connect(w ,...,stoarge,..., Qt::QueuedConnection);
H>QObject::connect(stoarge,...,w, Qt::QueuedConnection);
H>w.show();
H>storage.start();
H>}
H>

H>Что имею:
H> Первым же делом CBackGroundStorage принимается парсить файлы (очень длительный процесс), но GUI при этом подвисает и лог сообщения не показывает — я так понимаю, что никакой многопоточности фактически нет

H>Вопрос — что делать, чтоб многопоточность появилась? У меня такое подозрение, что что-то в корне не так. Если не понятно то буду писать минимумпример



Чтоб слоты отрабатывались в другом потоке, этот другой поток должен содержать цикл обработки сообщений. Т.о. в QThread::run() нужно вызвать QThread::exec()

Ну и, как уже отмечали выше, main() у тебя не правильно составлен. Он тоже должен содержать цикл обработки сообщений для основного потока, т.е. QApplication::exec()
А также надо написать код завершения потока при выходе из приложения (хотя, возможно, Qt сам умеет разбираться с этим, надо глянуть в исходниках деструктор класса QThread).
Прошлое забыто. Будущее закрыто. Настоящее даровано. (с) одна мудрая черепаха
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.