Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > С/С++: Кроссплатформенное программирование, Qt/Gtk+/wxWidgets > QThread поток не завершается


Автор: loneybibi 24.1.2012, 12:23
Добрый день! smile 
Первый раз пробую что такое потоки. Так что сильно не ругать! smile 

Собственно вот класс потока:
Код

class mThread:public QThread{
    Q_OBJECT
public:
    void run(){
        QSqlDatabase db=QSqlDatabase::addDatabase("QMYSQL");
        db.setHostName("localhost");
        db.setDatabaseName("dbtest");
        db.setUserName("admin");
        db.setPassword("qwerty");
        db.open();

        QSqlTableModel table;
        table.setTable("table1");
        table.select();

        emit done(&table);

        exec();
    }

signals:
    void done(QSqlTableModel *model);
};


В главном потоке с GUI перед его удалением выполняю выход из потока:
Код

MainWindow::~MainWindow()
{
    th->exit();
    th->wait();
    delete ui;
}

Но почему то все равно пишет ошибку и выход из потока не происходит:
Код

Error in my_thread_global_end(): 1 threads didn't exit

Да и собственно само приложение теперь завершается очень долго после QMainWindow::close() проходит около 4 секунд.

Объясните где тут ошибка и что тут не так пожалуйста! smile

Прикрепил проект на всякий случай! smile

Автор: borisbn 24.1.2012, 12:36
Может дело и не в этом, но классы, наследуемые от QObject (а твой mThread такой, т.к. наследуется от QThread, который наследуется от QObject), так вот, такие классы должны быть описаны в отдельном h-нике. Это нужно для moc-компилятора.
Короче, попробуй вынести объявление mThread в отдельный h-ник и включи его в mainwindow.h

Автор: loneybibi 24.1.2012, 12:41
Цитата(borisbn @ 24.1.2012,  11:36)
Может дело и не в этом, но классы, наследуемые от QObject (а твой mThread такой, т.к. наследуется от QThread, который наследуется от QObject), так вот, такие классы должны быть описаны в отдельном h-нике. Это нужно для moc-компилятора.
Короче, попробуй вынести объявление mThread в отдельный h-ник и включи его в mainwindow.h

Попробовал все тоже самое.

Автор: kuzulis 24.1.2012, 13:00
А если в run() оставить только:
Код

exec();

то что?

Скорее всего это изза:
Код

signals:
    void done(QSqlTableModel *model);

Зачем использовать указатель на объект, созданный в другой нити из главной нити?

Если уж создал QSqlDatabase, QSqlTableModel  в mThread то они там и должны оставаться,
не нужно выносить "мусор из избы" (с)

А хотя, може быть, из-за того, что некому делать db.close()...
Ты повыкидывай (позакомментируй) в run() разные строчки и узнаешь из-за чего.

Автор: loneybibi 24.1.2012, 13:33
Цитата(kuzulis @  24.1.2012,  12:00 Найти цитируемый пост)
А если в run() оставить только:
код C++
1:
exec();

то что?

Если оставить только это то все нормально.

Цитата(kuzulis @  24.1.2012,  12:00 Найти цитируемый пост)
Зачем использовать указатель на объект, созданный в другой нити из главной нити?


Есть QMainWindow на нем QTableView. Раньше это все было в главном потоке. Но так как процесс выборки данных довольно длительный и интерфейс подвисает при этом, я решил вынести выборку данных в отдельную нить, дабы дать пользователю работать дальше а не ждать пока запрос данных закончится. 

Если оставлять все внутри нити то как тогда задать для QTableView модель данных ? Или как тогда это правильно сделать ? 

Цитата(kuzulis @  24.1.2012,  12:00 Найти цитируемый пост)
А хотя, може быть, из-за того, что некому делать db.close()...

Я думаю тут делать закрытие соединения не прицепиально т.к. объект создан в стеке, да и db.close ничего не меняет.
И если бы соединение не закрывалось то на консоль был бы характерный вывод о том что соединение все еще используется.

Автор: loneybibi 24.1.2012, 14:11
В справке по Qt функция run() переопределялась как public:
Код

To create your own threads, subclass QThread and reimplement run(). For example:

 class MyThread : public QThread
 {
 public:
     void run();
 };

 void MyThread::run()
 {
     QTcpSocket socket;
     // connect QTcpSocket's signals somewhere meaningful
     ...
     socket.connectToHost(hostName, portNumber);
     exec();
 }

Открыл вторую редакцию книги Макса Шлее по Qt.
Увидел что там в примере, функция run() переопределяется как protected:
Код

class Thread : public QThread
{
    Q_OBJECT
public:
    Thread();
    void setMessage(const QString &message);
    void stop();
protected:
    void run();
private:
    QString messageStr;
    volatile bool stopped;
};


Вообщем перенес свою функцию в protected и все работает как часы:
Код

class mThread:public QThread{
    Q_OBJECT

protected:
    void run(){
        QSqlDatabase db=QSqlDatabase::addDatabase("QMYSQL");
        db.setHostName("localhost");
        db.setDatabaseName("dbtest");
        db.setUserName("admin");
        db.setPassword("qwerty");
        db.open();

        QSqlTableModel table;
        table.setTable("media_data");
        table.select();

        emit done(&table);

        db.close();
        
        qDebug()<<">>>> Thread done";

        exec();
    }

signals:
    void done(QSqlTableModel *model);
};


Всем спасибо! smile

Автор: Sahab 24.1.2012, 18:33
без разницы протектед или паблик. видимо изменил что-то ненароком)

Добавлено через 2 минуты и 59 секунд
мало того, на куте-лабсе был пост о том, что не нужно так делать.
правильно - создавать объект db (инкапсулирующий работу с БД) и moveToThread ( QThread * targetThread ) в необходимый поток.

Автор: borisbn 24.1.2012, 18:39
Цитата(Sahab @  24.1.2012,  18:33 Найти цитируемый пост)
без разницы протектед или паблик. видимо изменил что-то ненароком)

 smile 
в исполняемом коде эти надписи
Код
public: void run(); // 1
protected: void run(); // 2

абсолютно одинаковы.
Проверь - измени сейчас в рабочем коде обратно protected на public и всё будет работать

Автор: loneybibi 24.1.2012, 22:13
Цитата(borisbn @ 24.1.2012,  17:39)
Цитата(Sahab @  24.1.2012,  18:33 Найти цитируемый пост)
без разницы протектед или паблик. видимо изменил что-то ненароком)

 smile 
в исполняемом коде эти надписи
Код
public: void run(); // 1
protected: void run(); // 2

абсолютно одинаковы.
Проверь - измени сейчас в рабочем коде обратно protected на public и всё будет работать

Я конечно не совсем уверен но они точно имеют различия. По логике зачем бы тогда был раздел ptotected и public.
И извините меня конечно то тот проект что я выложил в первом посте и в конечно что я описал ничем не отличается
кроме того что я перенес функцию run() в раздел protected!! 

Если мне не верите просто возьмите код из первого моего проекта и перенесите функцию run() в раздел protected! 
Просто проверьте! 

Я благодарен за ответы и я только начал изучать нити но это действительно так!

Добавлено @ 22:19
Смыла врать мне нет. borisbn  возьмите мой код и он будет работать с ошибкой. А если изменить на протектед то все ок ! Просто попробуйте! Win xp sp3 qt 4.7.4

Автор: loneybibi 24.1.2012, 23:43
Цитата(Sahab @ 24.1.2012,  17:33)
без разницы протектед или паблик. видимо изменил что-то ненароком)

Добавлено @ 18:36
мало того, на куте-лабсе был пост о том, что не нужно так делать.
правильно - создавать объект db (инкапсулирующий работу с БД) и moveToThread ( QThread * targetThread ) в необходимый поток.

поясните примером пожалуйста как инкапсулировать и как переместить поток ! ? smile
про инкапсуляции в поток и тд я еще не в курсе т.к. только начал изучать нити как и сказал выше ! smile 
Буду благодарен за примеры ! 

Автор: loneybibi 25.1.2012, 00:00
извините.

Фух че то я запутался!
очистил проект. 
перенес обратно в паблик функцию run().
собрал, все работает! но код тот же ! 
почему не работало до этого не понял вообще не понял!
не менял ничего честно !

объясните пожалуйста тогда в чем разница между public и protected если
для компилятора это один и тот же код ? только в зоне видимости объектов? 

Автор: borisbn 25.1.2012, 09:38
Цитата(loneybibi @  25.1.2012,  00:00 Найти цитируемый пост)
объясните пожалуйста тогда в чем разница между public и protected еслидля компилятора это один и тот же код ? только в зоне видимости объектов? 

Разница только в том, выдавать ли ошибку при компиляции, если кто-то пытается вызвать protected-функцию снаружи (или при private-наследовании... брррр...). В исполняемом же коде (ессно, если скомпилировалось без ошибок) разницы нет вообще никакой

Автор: daemonaz 10.2.2012, 11:22
Скажите пожалуйста как получить результат завершения потока? Я в потоке в случае заверщения вызываю функцию exit(int), аргумент есть код возврата, мне нужно от потока получить этот код.

Автор: loneybibi 10.2.2012, 13:41
Цитата(daemonaz @ 10.2.2012,  10:22)
Скажите пожалуйста как получить результат завершения потока? Я в потоке в случае заверщения вызываю функцию exit(int), аргумент есть код возврата, мне нужно от потока получить этот код.

В переопределении функции run() есть exec() который запускает очередь сообщений потока вот она и возвращает код возврата, как в main.cpp  функции main() в принципе.

Автор: daemonaz 10.2.2012, 14:53
loneybibi,  я не понял, зачем exec возврашать? Это же eventloop, или я чего то недопонимаю..
Запускаю поток, в котором нет бесконечного цикла, и exec я не использую, но в случае ошибки поток прерывает работу с помошью метода exit(int), нужно как только поток завершил работу, приложение, которое его запустило, должно проанализировать ошибку и выдать сообщение юзеру что произошла авария в работе потока.

Автор: loneybibi 11.2.2012, 01:56
Цитата(daemonaz @ 10.2.2012,  13:53)
loneybibi,  я не понял, зачем exec возврашать? Это же eventloop, или я чего то недопонимаю..
Запускаю поток, в котором нет бесконечного цикла, и exec я не использую, но в случае ошибки поток прерывает работу с помошью метода exit(int), нужно как только поток завершил работу, приложение, которое его запустило, должно проанализировать ошибку и выдать сообщение юзеру что произошла авария в работе потока.

Я бы добавил в класс потока макрос Q_OBJECT и сделал сигнал по завершению выполнения функций потока.
Например так:
Код

class MyThread : public QThread{
    Q_OBJECT
public:
    virtual void run(){
        int error=0;

        if("условие"){
             //если все ок то выполняем то что нужно
       }else{
            //если нет то пишем в error код ошибки.
            error=5;
       }
        
        emit threadDone(error);
    }

signals:
    void threadDone(int result);
};

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)