Russian Qt Forum
Май 17, 2024, 03:46 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Главный поток умирает по непонятной причине.  (Прочитано 10073 раз)
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?
Записан
lit-uriy
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3880


Просмотр профиля WWW
« Ответ #1 : Февраль 12, 2010, 22:21 »

>>emit t.setI(200);
Зачем пишешь слово "emit" в функции main?
Записан

Юра.
lit-uriy
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3880


Просмотр профиля WWW
« Ответ #2 : Февраль 12, 2010, 22:24 »

>>А если первой командой в main() создавать объек QApplication, то я получаю на выходе всего три строки:
А запуск цикла событий:
app.exec()
где?
Записан

Юра.
SABROG
Гость
« Ответ #3 : Февраль 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;
« Последнее редактирование: Февраль 12, 2010, 22:43 от SABROG » Записан
White Owl
Гость
« Ответ #4 : Февраль 12, 2010, 23:02 »

...
Читал, думал... ничего не понял.
Не, я кажется понял что происходит, но не понял как это исправить.

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

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

Код:
main finished

А именно сразу после return 0;
А у меня она в памяти висеть не остается... Играюсь с 4.6.1 на Виндах.
Записан
SABROG
Гость
« Ответ #5 : Февраль 12, 2010, 23:13 »

В общем я вырубил Mass Effect 2 (программа стала нормально выходить) и проверил твой пример еще раз - поведение одинаковое как с QApplication так и без. Всё выводится в консоль и работает. Но как я уже говорил тут сплошной прямой вызов, никаких сигналов и слотов через эвенты не передается.
Записан
White Owl
Гость
« Ответ #6 : Февраль 13, 2010, 00:21 »

Ok... я уже понял что наврал в своем тесте на полную катушку. Но все таки мой главный вопрос остается пока без ответа:

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

Сообщений: 60


Просмотр профиля
« Ответ #7 : Февраль 13, 2010, 00:56 »

Сам обьект MyThread со своими переменными находиться в потоке главном потоке QApplication.
Вставь в конструкторе MyThread
Код:
moveToThread(this)
Записан
lit-uriy
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3880


Просмотр профиля WWW
« Ответ #8 : Февраль 13, 2010, 01:33 »

Цитировать
Другое дело, что слот вызывается не объекта, который находится в другом потоке, поэтому вызов t.setI() всегда будет DirectConnect, как прямой callback.
В данной ситуации слот следует воспринимать как вызов обычной функции-члена класса, и только. А emit применительно к слоту вообще несуразица.
Записан

Юра.
White Owl
Гость
« Ответ #9 : Февраль 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

Записан
SABROG
Гость
« Ответ #10 : Февраль 13, 2010, 03:50 »

Тебе нужно создавать сигнал или делать реализацию на основе эвентов. Запись "emit slot()" бессмысленна, она работает как обычная функция и ничего в очередь не ставит. Если нужно вызвать слот без сигнала через очередь, то пользуйся QMetaObject::invokeMethod().
Записан
mcrads
Гость
« Ответ #11 : Февраль 13, 2010, 15:51 »

как делал я. я передавал в конструктор потока параметры из главного потока которые необходимо изменить. далее я значения этих параметров присваивал внутренним переменным потока, работал с ними. потом вызывал передачу значений опять в параметры и останавливал поток из себя.
Если не ошибаюсь, передачу параметров можно сделать так же обращением parent.parametr = ... в случае если ты задашь тип родительского объекта не QObject а свой изначальный и опишешь параметры как публичные. Такой метод у меня тоже работал вполне сносно. потоки позволяют много чего.
реализация QEventLoop считаю вообще здесь ни к чему. поиграйся. и не забывай про отладку по шагам =)
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Февраль 15, 2010, 04:50 »

реализация QEventLoop считаю вообще здесь ни к чему.
Да вот как раз "к чему"  Улыбающийся Слот/сигнал между нитками выполняется только через eventLoop. И QDirectConnection тоже. Примерно таким образом:

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

Так что все eventLoop должны быть запущены
Записан
White Owl
Гость
« Ответ #13 : Февраль 15, 2010, 21:16 »

Вот, нашел в сети такую статью и там даже есть пример.
http://www.linuxjournal.com/article/9602
Кажется все мои вопросы про создание фоновых потоков изображающих сервер отвечены.
В FAQ ee! В FAQ! Улыбающийся

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


Страница сгенерирована за 0.137 секунд. Запросов: 23.