Russian Qt Forum

Qt => Общие вопросы => Тема начата: White Owl от Февраль 12, 2010, 21:43



Название: Главный поток умирает по непонятной причине.
Отправлено: White Owl от Февраль 12, 2010, 21:43
Играюсь с QThread:

main.cpp:
Код:
void sleep(int msec) {
QTime tt;
tt.start();
while(tt.elapsed() < msec);
}

int main(int argc, char ** argv) {
qDebug() << "main started";
// QApplication app(argc, argv);
MyThread t;

t.start();
sleep(100);
emit t.setI(5);
sleep(100);
emit t.setI(10);
sleep(100);
emit t.setI(200);
sleep(100);
while(!t.isFinished()) {
sleep(100);
}
qDebug() << "main finished";
return 0;
}

MyThread.h
Код:
#ifndef MYTHREAD_H
#define MYTHREAD_H

#include <QThread>
#include <QEventLoop>

class MyThread : public QThread {
Q_OBJECT
int i;
QEventLoop eventLoop;
public:
MyThread();
protected:
void run();
public slots:
void setI(int i);
};

#endif // MYTHREAD_H
MyThread.cpp
Код:
#include "MyThread.h"
#include <QDebug>

MyThread::MyThread() {
i=0;
}

void MyThread::run() {
qDebug() << "thread started";
while(i<100) {
qDebug() << i;
eventLoop.processEvents();
}
qDebug() << "thread finished";
}

void MyThread::setI(int newI) {
i = newI;
}

Если запустить этот код как есть (то есть без создания объекта QApplication), то на выходе я получаю:
Debug: main started
Warning: QEventLoop: Cannot be used without QApplication
Debug: thread started
Debug: 0
..... много строк
Debug: 0
Debug: 5
..... много строк
Debug: 5
Debug: 10
..... много строк
Debug: 10
Debug: thread finished
Debug: main finished

А если первой командой в main() создавать объек QApplication, то я получаю на выходе всего три строки:
Debug: main started
Debug: thread started
Debug: 0


Спрашивается: что происходит? Что я делаю неправильно?
Как правильно использовать QEventLoop?


Название: Re: Главный поток умирает по непонятной причине.
Отправлено: lit-uriy от Февраль 12, 2010, 22:21
>>emit t.setI(200);
Зачем пишешь слово "emit" в функции main?


Название: Re: Главный поток умирает по непонятной причине.
Отправлено: lit-uriy от Февраль 12, 2010, 22:24
>>А если первой командой в main() создавать объек QApplication, то я получаю на выходе всего три строки:
А запуск цикла событий:
app.exec()
где?


Название: Re: Главный поток умирает по непонятной причине.
Отправлено: SABROG от Февраль 12, 2010, 22:31
Юрий, я думаю тут несколько сложнее ситуация. emit сам по себе пустышка, лишь указатель типа тут мы вызываем слот или испускаем сигнал. Другое дело, что слот вызывается не объекта, который находится в другом потоке, поэтому вызов t.setI() всегда будет DirectConnect, как прямой callback. Сам экземпляр класса MyThread тоже принадлежит главному потоку, также как и i и QEventLoop.

С помощью метод eventLoop.processEvents(); автор пытается обрабатывать события. Но их там быть не может по определению. А раз их там нет, то я не понимаю откуда тут deadlock, если processEvents() по умолчанию не ждет поступления каких либо событий, а должен возвращаться сразу же обратно, если очередь пуста.

С вариантом когда нет QApplication я еще могу понять почему всё работает. Скорее всего потому, что processEvents() не найдя действующей инстанции QCoreApplication просто возвращается, а прямые вызовы t.setI() делают своё черное дело. Т.е. очередь никак не задействована.
---

В общем как обычно. Проверил код у себя программа ведет себя одинаково при QApplication и его отсутствии за одним исключением. Если есть QApplication/QCoreApplication, то программа просто остается висеть в памяти после сообщения:

Код:
main finished

А именно сразу после return 0;


Название: Re: Главный поток умирает по непонятной причине.
Отправлено: White Owl от Февраль 12, 2010, 23:02
...
Читал, думал... ничего не понял.
Не, я кажется понял что происходит, но не понял как это исправить.

Есть где-нибудь простенький пример как надо правильно создавать фоновые потоки чтобы они работали как сервер? Чтоб оно висело в ждущем режиме, главный поток давал запрос и оставался рисовать ГУИ, фоновый поток проделав долгую операцию кидал результаты главному потоку и засыпал.

В общем как обычно. Проверил код у себя программа ведет себя одинаково при QApplication и его отсутствии за одним исключением. Если есть QApplication/QCoreApplication, то программа просто остается висеть в памяти после сообщения:

Код:
main finished

А именно сразу после return 0;
А у меня она в памяти висеть не остается... Играюсь с 4.6.1 на Виндах.


Название: Re: Главный поток умирает по непонятной причине.
Отправлено: SABROG от Февраль 12, 2010, 23:13
В общем я вырубил Mass Effect 2 (программа стала нормально выходить) и проверил твой пример еще раз - поведение одинаковое как с QApplication так и без. Всё выводится в консоль и работает. Но как я уже говорил тут сплошной прямой вызов, никаких сигналов и слотов через эвенты не передается.


Название: Re: Главный поток умирает по непонятной причине.
Отправлено: White Owl от Февраль 13, 2010, 00:21
Ok... я уже понял что наврал в своем тесте на полную катушку. Но все таки мой главный вопрос остается пока без ответа:

Есть где-нибудь простенький пример как надо правильно создавать фоновый поток чтобы он работал как сервер?
Фоновый поток должен висеть в ждущем режиме, главный поток будет посылать запрос (или набор запросов в очередь) и рисовать ГУИ, фоновый поток проделав долгую операцию кидал результаты главному потоку и засыпал.


Название: Re: Главный поток умирает по непонятной причине.
Отправлено: ax от Февраль 13, 2010, 00:56
Сам обьект MyThread со своими переменными находиться в потоке главном потоке QApplication.
Вставь в конструкторе MyThread
Код:
moveToThread(this)


Название: Re: Главный поток умирает по непонятной причине.
Отправлено: lit-uriy от Февраль 13, 2010, 01:33
Цитировать
Другое дело, что слот вызывается не объекта, который находится в другом потоке, поэтому вызов t.setI() всегда будет DirectConnect, как прямой callback.
В данной ситуации слот следует воспринимать как вызов обычной функции-члена класса, и только. А emit применительно к слоту вообще несуразица.


Название: Re: Главный поток умирает по непонятной причине.
Отправлено: White Owl от Февраль 13, 2010, 01:52
Сам обьект MyThread со своими переменными находиться в потоке главном потоке QApplication.
Вставь в конструкторе MyThread
Код:
moveToThread(this)
Вставил. Получил:
main started
thread started
0
ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 22ff30. Receiver '
' (of type 'QCoreApplication') was created in thread 3e27a8", file kernel\qcoreapplication.cpp, line 347



Название: Re: Главный поток умирает по непонятной причине.
Отправлено: SABROG от Февраль 13, 2010, 03:50
Тебе нужно создавать сигнал или делать реализацию на основе эвентов. Запись "emit slot()" бессмысленна, она работает как обычная функция и ничего в очередь не ставит. Если нужно вызвать слот без сигнала через очередь, то пользуйся QMetaObject::invokeMethod().


Название: Re: Главный поток умирает по непонятной причине.
Отправлено: mcrads от Февраль 13, 2010, 15:51
как делал я. я передавал в конструктор потока параметры из главного потока которые необходимо изменить. далее я значения этих параметров присваивал внутренним переменным потока, работал с ними. потом вызывал передачу значений опять в параметры и останавливал поток из себя.
Если не ошибаюсь, передачу параметров можно сделать так же обращением parent.parametr = ... в случае если ты задашь тип родительского объекта не QObject а свой изначальный и опишешь параметры как публичные. Такой метод у меня тоже работал вполне сносно. потоки позволяют много чего.
реализация QEventLoop считаю вообще здесь ни к чему. поиграйся. и не забывай про отладку по шагам =)


Название: Re: Главный поток умирает по непонятной причине.
Отправлено: Igors от Февраль 15, 2010, 04:50
реализация QEventLoop считаю вообще здесь ни к чему.
Да вот как раз "к чему"  :) Слот/сигнал между нитками выполняется только через eventLoop. И QDirectConnection тоже. Примерно таким образом:

- посылающий в др. нитку никак не может ее "остановить", поэтому он посылает событие в очередь этой нитки. Если QDirectConnection установлен, то посылающий ждет на семафоре пока событие будет обработано. Когда это случится - зависит от нитки-приемника, если она игнорирует свой eventLoop - то никогда. С точки зрения посылающего все правильно, QDirectConnection есть синхронный способ: послал - получил ответ - продолжил выполнение.

Так что все eventLoop должны быть запущены


Название: Re: Главный поток умирает по непонятной причине.
Отправлено: White Owl от Февраль 15, 2010, 21:16
Вот, нашел в сети такую статью и там даже есть пример.
http://www.linuxjournal.com/article/9602
Кажется все мои вопросы про создание фоновых потоков изображающих сервер отвечены.
В FAQ ee! В FAQ! :)

Осталось только понять как объект может принадлежать потоку, и зачем это собственно говоря делается?
Вроде ж у всех нитей одного процесса общая память и только стеки разные? Или аналогия с fork()/CreateThread() в QThread не играет?