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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Проблема последовательности запуска слота при комуникации потоков  (Прочитано 3853 раз)
yashaka
Гость
« : Апрель 07, 2010, 14:03 »

Итак, такая вот загогулина:

Вспомогательный поток (CalibrateThread):

Код:
void CalibrateThread::setCommand(const QString commandStr, const int pause)
{
    command = commandStr;
    this->pause = pause;
    /*DEBUG*/log("setCommand(): at exit(0);");
    exit(0);
    /*DEBUG*/log("setCommand(): after exit(0);");
}

...

Код:
void CalibrateThread::run()
{
    forever {
        /*DEBUG*/log("run(): forever {...}: at emit readyToGetCommand();");
        emit readyToGetCommand();
        /*DEBUG*/log("run(): forever {...}: at exec();");
        exec();
        /*DEBUG*/log("run(): forever {...}: after exec();");
    }
}

Главний поток (MainWindow):

Код:
calibrator = new CalibrateThread;
...
connect(calibrator, SIGNAL(readyToGetCommand()), this, SLOT(runNextTest()));
connect(thid, SIGNAL(commandToSet(const QString, const int)), calibrator, SLOT(setCommand(const QString, const int)));
...
void MainWindow::runNextTest()
{
    ...
    /*DEBUG*/log("runNexTest(): at emit commandToSet(command, pause);");
    emit commandToSet(command, pause);
}

Так вот, при работе программы происходит следующее:
Вариант 1 - если поток calibrator запускается с приоритетом выше чем главный
Все работает как "хочется", то есть: Калибратор посылает главному потоку сигнал readyToGetCommand,  что готов принять команду и запускает еxec(), то есть ждет сигнала от главного с командой. Далее главный получает сигнал от калибратора и отвечает ему сигналом с командой, который приводит к запуску слота калибратора setCommand(...) в котором и запускается exit(0), что и приводит к выходу из exec() в калибраторе.

Вариант 2 - если поток calibrator запускается с приоритетом меньше чем главный
Програма зависает... Она не может выйти с exec() в калибраторе, так как всю свою работу (получение сигнала и отправка ответного с вызовом слота setCommand()) главный успел сделать еще до exec() в калибраторе.

Итак вопрос: почему так происходит? Ведь при конекшене сигнала commandToSet() cо слотом setCommand() используется AutoConnection что в даном случае равно QueuedConnection, а это ведь должно значить, что слот выполнится не сразу, а когда ивент луп ему даст это сделать, то есть тогда когда запустится exec()... А выходит что слот запускается еще до этого...

Это видно по крайней мере из того что пишется в логе, то есть что то типа такого:
run(): forever {...}: at emit readyToGetCommand();
runNexTest(): at emit commandToSet(command, pause);
setCommand(): at exit(0);
setCommand(): after exit(0);
run(): forever {...}: at exec();

КОНЕЦ:) то есть тут программа и начинает висеть.

Вообще то есть еще и второй прикол, а именно в том, что когда при ВАРИАНТЕ 1 все работает то лог выглядит так:
run(): forever {...}: at emit readyToGetCommand();
runNexTest(): at emit commandToSet(command, pause);
setCommand(): at exit(0);
setCommand(): after exit(0);
run(): forever {...}: at exec();
run(): forever {...}: after exec();


тоесть программа не виснит, выходит из екзека, !но почему то лог не выглядит  хотя бы вот так:
run(): forever {...}: at emit readyToGetCommand();
runNexTest(): at emit commandToSet(command, pause);
run(): forever {...}: at exec();
setCommand(): at exit(0);
setCommand(): after exit(0);
run(): forever {...}: after exec();


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

Сейчас главное, на что я хочу получить ответ - это почему "не работает" queuedconnection, и если действительно это не моя ошибка, а это так и должно быть, то как тогда мне реализовать такую комуникацию чтобы все работало...
Записан
SABROG
Гость
« Ответ #1 : Апрель 16, 2010, 16:14 »

Итак вопрос: почему так происходит? Ведь при конекшене сигнала commandToSet() cо слотом setCommand() используется AutoConnection что в даном случае равно QueuedConnection, а это ведь должно значить, что слот выполнится не сразу, а когда ивент луп ему даст это сделать, то есть тогда когда запустится exec()... А выходит что слот запускается еще до этого...
QueuedConnection только если 2 объекта в разных потоках. exec() запускает локальный цикл событий в потоке, но существует еще и основной цикл событий в GUI'шном потоке, поэтому все сигналы, которые отсылаются из порожденного потока, где не запущен exec() спокойно доходят до объекта существующего в главном потоке.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Апрель 16, 2010, 22:11 »

Проверьте что CalibrateThread::setCommand выполняется где надо, а не в главной нитке. Это легко сделать распечатав QThread::currentThread() в 2-х местах
Записан
yashaka
Гость
« Ответ #3 : Апрель 19, 2010, 09:34 »

Проверил!
Действительно, CalibrateThread::setCommand выполняется в главном потоке...

Спасибо за "просветление"Улыбающийся

Теперь конечно все стало ясно...
Переписал код, теперь использую вейткондишены, все работает...

Но сейчас для меня полная загадка, в каких же тогда случаях может возникнуть ситуация с именно QueuedConnection и при каких условиях (при каких я понял - когда объекты в разных потоках, имею ввиду примеры...) его можно использовать...
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Апрель 19, 2010, 11:40 »

Но сейчас для меня полная загадка, в каких же тогда случаях может возникнуть ситуация с именно QueuedConnection и при каких условиях (при каких я понял - когда объекты в разных потоках, имею ввиду примеры...) его можно использовать...
В исходниках пишут
Код:
   // check connection type
    QThread *currentThread = QThread::currentThread();
    QThread *objectThread = object->thread();
    if (connectionType == Qt::AutoConnection) {
        connectionType = currentThread == objectThread
                         ? Qt::DirectConnection
                         : Qt::QueuedConnection;
    }
CalibratorThread был создан в главной нитке, значит currentThread == objectThread при вызове setCommand имеем DirectConnection. А вот при вызове readyToGetCommand уже currentThread != objectThread, значит QueuedConnection

На мой взгляд Вы перемудрили  Улыбающийся Все получается гораздо проще если строить управление "от сервера". Создали CalibratorThread и запустили в ней exec (один раз на всю ее жизнь). Это просто run по умолчанию. От сервера посылаете сигнал (команду), CalibratorThread ее отрабатывает и посылает сигнал "готово", сервер подает след. команду и.т.д. Когда нитка больше не нужна - сервер сделал ей quit - и все дела.
Записан
yashaka
Гость
« Ответ #5 : Апрель 19, 2010, 14:12 »

Цитировать
CalibratorThread был создан в главной нитке, значит currentThread == objectThread при вызове setCommand имеем DirectConnection. А вот при вызове readyToGetCommand уже currentThread != objectThread, значит QueuedConnection

Улыбающийся Наконец то до меня дошло полностью.

Цитировать
На мой взгляд Вы перемудрили   Все получается гораздо проще если строить управление "от сервера". Создали CalibratorThread и запустили в ней exec (один раз на всю ее жизнь). Это просто run по умолчанию. От сервера посылаете сигнал (команду), CalibratorThread ее отрабатывает и посылает сигнал "готово", сервер подает след. команду и.т.д. Когда нитка больше не нужна - сервер сделал ей quit - и все дела.

Очень даже может быть, что перемудрил:) По крайней мере, ваше предложение - намного элегантнее. Пока оставлю свою реализацию - как есть, но на следующий раз обязательно сначала попробую следовать вашему предложению. Большое спасибо за напутствия!
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


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