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

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

Страниц: [1] 2 3 4   Вниз
  Печать  
Автор Тема: Процесс замерзает при работе интерфейса  (Прочитано 23008 раз)
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« : Ноябрь 13, 2014, 00:24 »

Столкнулся со странным поведением при отладке трех взаимодействующих процессов. Бутерброд толстый, весь код нереально показать, проще в общем описать.

1я нить выполняется циклически и пихает в сигнал разнообразные данные, упакованные в QVariant - пока проверяю со строками, целыми и вещественными числами. Сигнал по определению Queued. Эта нить находится в отдельной загружаемой DLL.
В другой загружаемой DLL в пространстве "основного" процесса приложения находится приемник сигнала, который получает данные, распознает их тип, и запускает на выполнение 3ю нить, обрабатывающую по-разному данные разного типа. Причем запускает он её не простым стартом QThread, а снятием ранее запущенной нити, которая к этому времени находится в WaitCondition. То есть, основной процесс-приемник сигнала делает wakeOne() для ожидающей нити, после того, как разобрал данные и подготовил для обработки. И сразу, как только он разбудил обработчик, основной процесс приостанавливается на своём WaitCondition (иначе он начинает ловить следующие входящие сигналы, и начинается чехарда при подготовке данных, которые никто не успевает обработать). Когда обработка закончена, 3я нить снимает основной процесс с ожидания вызовом wakeOne() сама снова становится в WaitCondition, и приём повторяется.

Это всё очень славно работает, пока... не пошевелишь мышкой. Стоит это сделать - не понятно по какой причине интерфейс замерзает, но не полностью. Приложение реагирует на кнопку закрытия, причем не тупо закрывается, а нормально отрабатывает все заложенные функции (а там производится рассылка команды "умри" нескольким плагинам, которые еще и отвечают потом, что они умерли, и только после этого приложение закрывается - это всё выполняется). Более того, судя по потреблению процессора и посвистыванию дросселей материнки, 1я нить, которая отправляет данные, продолжает работать - очередь наполняется, свободная память при этом потихоньку исчезает. Ощущение такое, будто обрабатывающая нить ушла в WaitCondition, а основной процесс её почему-то оттуда не снимает. Совершенно точно нет состояния dead-lock - в нём приложение у меня нормально не закрывается, это проверенно. И при dead-lock в отладчике появляется соответствующее сообщение - тут его нет.

Причем всё это на не самом слабеньком процессоре с 4-мя ядрами...

Вот вопрос - кто-нибудь сталкивался с чем-то подобным? Когда отработка интерфейса "сбивает" работу слотов, в которых используются функции синхронизации с помощью WaitCondition? Я делал такие синхронизации несколько раз, но ни разу именно так, и ни разу не влипал в подобное. Буду, конечно, пытаться изменить логику, но интересно - может есть какие-то известные ограничения?
« Последнее редактирование: Ноябрь 13, 2014, 00:27 от Гурман » Записан

2^7-1 == 127, задумайтесь...
vulko
Гость
« Ответ #1 : Ноябрь 13, 2014, 09:30 »

В UI'ном трэде что-то делается?
Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #2 : Ноябрь 13, 2014, 10:45 »

в UI (я его назвал основным процессом) слот принимает сигнал, определяет тип, декодирует данные, кладет их на специальный стек (всё это очень быстро, сотня строк без тяжелых вызовов), будит нитку обработчика и становится в ожидание следующего сигнала - наверно, в последнем проблема, но странно, что пока мышкой не повозишь, всё нормально
« Последнее редактирование: Ноябрь 13, 2014, 10:50 от Гурман » Записан

2^7-1 == 127, задумайтесь...
vulko
Гость
« Ответ #3 : Ноябрь 13, 2014, 10:54 »

в UI (я его назвал основным процессом) слот принимает сигнал, определяет тип, декодирует данные, кладет их на специальный стек (всё это очень быстро, сотня строк без тяжелых вызовов), будит нитку обработчика и становится в ожидание следующего сигнала - наверно, в последнем проблема, но странно, что пока мышкой не повозишь, всё нормально

ну вот в этом то и проблема. выноси этот функционал за пределы UI'ного треда, а основной поток используй только для обновления интерфейса (отображения данных) и взаимодействия с пользователем.

даже если нет тяжелых вызовов, все равно есть ожидание. в этом и проблема. ожидание нельзя делать в UI'ном треде, т.к. помимо твоего кода там происходит много чего связанного с интерфейсом. Постоянные ивенты, перерисовки, апдейты и прочее прочее...
Записан
Bepec
Гость
« Ответ #4 : Ноябрь 13, 2014, 10:57 »

Скорее всего у вас глушится UI поток сигналами.
Интерфейс замерзает - непонятное у вас описание.
То ли интерфейс у программы замерзает - она не реагирует на наведение/щелчки мышкой и прочее.
То ли у вас окно зависает - покрывается блюром и считается зависшим системой.

Но, думаю, что у вас просто где-то ошибка и/или всё же упущение количества вызовов.
Вот такие расплывчатые догадки в ответ на общее описание.

PS ++ к предыдущему оратору
Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #5 : Ноябрь 13, 2014, 11:12 »

Интерфейс замерзает - непонятное у вас описание.
То ли интерфейс у программы замерзает - она не реагирует на наведение/щелчки мышкой и прочее.
То ли у вас окно зависает - покрывается блюром и считается зависшим системой.

интерфейс не реагирует на нажатия и наведение, кроме кнопки закрытия окна (на неё реагирует), перерисовывается нормально

глушиться тестовыми сигналами UI не должен - они идут очень редко, специально с целью тестирования они посылаются каждые полсекунды, заглушить этим интерфейс невозможно

значит придется еще одну нить запускать, для приёма и декодирования сигналов - в нити обработчика это делать сложно, она физически в другой DLL находится, отлаженной, и написанной преимущественно на Си
Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #6 : Ноябрь 13, 2014, 11:39 »

Да, проблема в ожидании в основном процессе. Убрал его и сделал передающий сигнал Blocking, интерфейс стал нормально работать.
Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #7 : Ноябрь 13, 2014, 19:57 »

А шайтан... Если в QThread запустилась нитка, залочила некий мутекс, завершилась (но мутекс не разлочился), и потом снова запустилась (тот же объект QThread) - при повторном запуске, вместо dead-lock она в этом мутексе просто молча залипает. Объект тот же, но нитка другая, другой ThreadID. И разлочить после перезапуска этот мутекс не нельзя - слетает с ошибкой.

Что-то я не помню предупреждения об этом в документации, а просто так это не совсем очевидно.
Записан

2^7-1 == 127, задумайтесь...
Bepec
Гость
« Ответ #8 : Ноябрь 13, 2014, 20:22 »

QMutexLocker используйте.

И насколько я помню, повторные локи всегда такое вызывали Веселый Там где то строчка имеется что всегда нужно контролить мутекс, иначе будет ой ой ой.
Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #9 : Ноябрь 13, 2014, 21:11 »

QMutexLocker используйте.

не вполне понятно, чем он поможет, у меня нет необходимости синхронизировать мутекс с созданием и удалением чего либо, поскольку ничего не создается и не удаляется

И насколько я помню, повторные локи всегда такое вызывали Веселый Там где то строчка имеется что всегда нужно контролить мутекс, иначе будет ой ой ой.

само собой, но речь не об этом, а о том, что при разных последовательных запусках одного и того же QThread::run() получаются разные нити, которые не могут работать с одним и тем же мутексом, также как если бы это была одна и та же нить - а проблема у меня именно в этом
Записан

2^7-1 == 127, задумайтесь...
Bepec
Гость
« Ответ #10 : Ноябрь 13, 2014, 23:01 »

Локер поможет в том, что при завершении run автоматом разлочит мутекс. Он берёт контроль за его состоянием на себя ) и ваша проблема исчезает Улыбающийся

PS это кстати поведение совершенно нормально. мой первый проект с потоками получал данные из контейнера давно удалённой нити Улыбающийся
Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #11 : Ноябрь 13, 2014, 23:32 »

Локер поможет в том, что при завершении run автоматом разлочит мутекс. Он берёт контроль за его состоянием на себя ) и ваша проблема исчезает Улыбающийся

В руководстве нет ничего про разлочивание мутекса локером при завершении run(). Там вообще не описано никакой связи между QThread и МутексЛокером.
« Последнее редактирование: Ноябрь 13, 2014, 23:34 от Гурман » Записан

2^7-1 == 127, задумайтесь...
Bepec
Гость
« Ответ #12 : Ноябрь 13, 2014, 23:42 »

QMutexLocker блочит мутекс в своем конструкторе и разлочивает в деструкторе. Потому создавая в начале run QMutexLocker вы получаете гарантию, что в конце у вас будет разлоченный мутекс Улыбающийся
Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #13 : Ноябрь 13, 2014, 23:53 »

QMutexLocker блочит мутекс в своем конструкторе и разлочивает в деструкторе. Потому создавая в начале run QMutexLocker вы получаете гарантию, что в конце у вас будет разлоченный мутекс Улыбающийся

Нет такой возможности. QThread::run() и мутекс находятся в разных DLL с ручной загрузкой, то есть, в плагинах. Более того, выполняемый в run() код находится в третьем плагине. Любой из плагинов может отсутствовать. Код, где мутекс и синхронизируемый код умеют общаться, но приплетать туда еще и run() будет идеологически неверно. Этот run ничего не знает и не иммет права знать о загруженных плагинах, и вообще выглядит вот так

Код:
void RunThread::run()
{
    _main( argc, argv );
}

То есть, в той же нити, где мутекс лочится, негде разместить разлочивание, я над этим уже несколько часов голову ломаю. Я так понимаю, МутексЛокер не сработает, если его создать в одной нити, а удалить из другой? Собственно, должен случиться тот же ассерт, что и про попытке просто сделать лок и анлок мутекса в разных нитях.
« Последнее редактирование: Ноябрь 14, 2014, 00:00 от Гурман » Записан

2^7-1 == 127, задумайтесь...
vulko
Гость
« Ответ #14 : Ноябрь 14, 2014, 09:26 »

Я так понимаю, МутексЛокер не сработает, если его создать в одной нити, а удалить из другой? Собственно, должен случиться тот же ассерт, что и про попытке просто сделать лок и анлок мутекса в разных нитях.

Мьютекс локер это критическая секция.
Работает в рамках внутри { }.

Это тот же

{
mutex.lock();
// do something
mutex.unlock();
}

Только немного удобнее, особенно если всякие if else с return'ами.

Лок анлок мьютекса в разных потоках делать не просто можно, а нужно.


Цитировать
не вполне понятно, чем он поможет, у меня нет необходимости синхронизировать мутекс с созданием и удалением чего либо, поскольку ничего не создается и не удаляется

мьютекс не только для синхронизации создания и удаления.
мьютекс для синхронизации по разделяемому ресурсу. при этом ресурс может на самом деле быть вообще статичным, т.е. не меняться.

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

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


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