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

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

Страниц: [1]   Вниз
  Печать  
Автор Тема: Потоки и сообщения об ошибках  (Прочитано 5296 раз)
Serr500
Гость
« : Май 29, 2012, 20:39 »

Условие задачи. Имеется некоторое число потоков, которые условно назовём "потребителями" и один поток, который назовём "управляющим". Кроме того, в приложении есть поток, в котором находятся все графические элементы, условно назовём его "GUI-поток". "Потребители" в заранее неизвестные моменты времени могут генерировать сигнал ошибки, обработка которой зависит от ответа пользователя, т.е. должен появиться диалог, в котором пользователь нажмёт на одну из кнопок.

Что требуется. Требуется, чтобы "потребитель" приостановил своё выполнение на время обработки ошибки, т.е поток должен быть "заморожен" в той точке, откуда испускается сигнал, ни один оператор "потребителя" после сигнала не должен выполняться. Вместо испускания сигнала (emit) может использоваться вызов метода "управляющего", указатель на который можно передать "потребителю". Далее этот сигнал должен быть получен "управляющим" и передан в "GUI-поток", который выведет диалог. После выбора пользователем действия сигнал проходит по цепочке в обратном направлении, т.е. "управляющий" получает ответ от "GUI-потока", выполняет с ним некоторые действия и передаёт ответ (возможно, модифицированный) "потребителю", который продолжит свою работу. "Управляющий" поток блокироваться не должен, максимум - кратковременная блокировка на время выполнения некоторых методов, полной блокировки потока вплоть до ожидания ответа пользователя быть не должно, "управляющий" должен быть готов обработать в это время другие сигналы.

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

Собственно вопрос: как реализовать такое поведение? В выборе объектов синхронизации ограничений нет - можно использовать любые. Ограничение лишь одно - использовать только Qt и С++. "Потребители" - простые потомки QThread без очереди сообщений, "управляющий" - потомок QThread с собственной очередью сообщений.

Заранее спасибо за любые подсказки и конструктивные предложения.
Записан
alexis031182
Гость
« Ответ #1 : Май 29, 2012, 20:50 »

Можно через семафоры.

В каждом "потребителе" объявить по семафору. Сразу после отправки сообщения для пользователя в GUI-поток (сообщение должно содержать указатель на семафор "потребителя") выполнить acquire() - "потребитель" заморозится. Как только GUI-поток отработает это сообщение, выполнить release() через полученный ранее указатель.
Записан
Serr500
Гость
« Ответ #2 : Май 29, 2012, 20:58 »

Можно через семафоры.
...
О! Это мысль. Почему-то меня потянуло в сторону мьютексов, а там возникала вероятность того, что сигнал успеет обработаться до вызова QMutex::lock(). С семафорами такой ситуации не возникнет. Если не обработалось, то QSemaphore::acquire() "тормознёт" "потребителя", а если успело обработаться - то QSemaphore::release() уже увеличит счётчик семафора и acqire проскочит без блокировки.
Огромное спасибо. Эта идея идеально решает одну часть задачи. Остаётся вторая - обработка сигналов по очереди.
Записан
alexis031182
Гость
« Ответ #3 : Май 29, 2012, 21:02 »

В гуи сообщения будут поступать по очереди. Их можно просто складировать в какой-нибудь контейнер и выполнять в гуи друг за другом, исходя из реакции пользователя. А семафоры не дадут своим соответствующим "потребителям" проснуться раньше времени.
Записан
Serr500
Гость
« Ответ #4 : Май 29, 2012, 21:14 »

А как бы это получше реализовать? Контейнер-то тоже надо защищать от одновременных операций. После выполнения обработки нужно будет удалить сообщение из очереди, а "управляющий" в это время может попытаться в очередь добавить новое. Как бы не получить "мёртвую блокировку". И как сообщать GUI, что ему нужно обработать новое сообщение? Кинуть сигнал?
Записан
alexis031182
Гость
« Ответ #5 : Май 29, 2012, 21:22 »

Можно отсылать события через postEvent() или сигналы через Qt::QueuedConnection. Тогда никаких блокировок контейнера делать не нужно (события появятся в гуи-потоке). В этих же событиях или сигналах и отправлять указатели на семафоры вместе с другими данными от "потребителя".
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Май 29, 2012, 21:23 »

А почему не задействовать тип соединения Qt::BlockingQueuedConnection?  Оно вроде именно для этого и предназначено
Записан
alexis031182
Гость
« Ответ #7 : Май 29, 2012, 21:26 »

Да, пожалуй лучше всего Улыбающийся
Записан
Serr500
Гость
« Ответ #8 : Май 29, 2012, 21:36 »

А почему не задействовать тип соединения Qt::BlockingQueuedConnection?  Оно вроде именно для этого и предназначено
Э-э-э... Тут проблема. Блокировка с "потребителя" снимется как только завершится выполнение слота в "управляющем". А он должен ещё будет передать сигнал дальше, в гуи. Но тогда надо блокировать и выполнение слота, т.е. тормозить "управляющего", а этого делать нельзя. Или как-то можно "разорвать" выполнение слота?

Можно отсылать события через postEvent() или сигналы через Qt::QueuedConnection. Тогда никаких блокировок контейнера делать не нужно (события появятся в гуи-потоке). В этих же событиях или сигналах и отправлять указатели на семафоры вместе с другими данными от "потребителя".
Так... Приблизительно понятно... То есть "потребитель" испускает сигнал (или вызывает метод "управляющего") и ждёт освобождения семафора. "Управляющий" помещает сигнал в очередь и испускает свой сигнал, который ловится в ГУИ. ГУИ обрабатывает сигнал и кидает свой с результатом операции "управляющему". Тот, в свою очередь, обрабатывает сигнал и освобождает семафор "потребителя". Я правильно понял последовательность действий?
Записан
alexis031182
Гость
« Ответ #9 : Май 29, 2012, 21:48 »

Э-э-э... Тут проблема. Блокировка с "потребителя" снимется как только завершится выполнение слота в "управляющем". А он должен ещё будет передать сигнал дальше, в гуи. Но тогда надо блокировать и выполнение слота, т.е. тормозить "управляющего", а этого делать нельзя. Или как-то можно "разорвать" выполнение слота?
Метод с блокирующим сигналом эффективен, если можно обойтись без "управляющего".

Так... Приблизительно понятно... То есть "потребитель" испускает сигнал (или вызывает метод "управляющего")
"Потребитель" и "управляющий" находятся в разных потоках, и если предполагается использовать контейнер событий именно в "управляющем", то нужно либо неблокирующее событие (QCoreApplication::postEvent), либо неблокирующий сигнал (Qt::QueuedConnection).

и ждёт освобождения семафора. "Управляющий" помещает сигнал в очередь и испускает свой сигнал, который ловится в ГУИ. ГУИ обрабатывает сигнал и кидает свой с результатом операции "управляющему". Тот, в свою очередь, обрабатывает сигнал и освобождает семафор "потребителя". Я правильно понял последовательность действий?
Да, правильно. Если "управляющий" обязателен, то именно так.
Записан
Serr500
Гость
« Ответ #10 : Май 29, 2012, 22:06 »

Метод с блокирующим сигналом эффективен, если можно обойтись без "управляющего".
К сожалению, нельзя. Именно "управляющий" в итоге решает, какой ответ получит "потребитель". Он принимает решения не только на основании ответа пользователя, но и учитывает некоторые другие факторы. Например, он получает сигналы от ещё одного потока - "производителя". Архитектура такова, что "управляющий" нужен. Может быть, удастся его объединить с "GUI-потоком", но это другой вопрос. Пока не знаю, удастся ли это сделать. Не хочется оказаться в ситуации "заморозки" пользовательского интерфейса. Хотя "управляющего" крайне нежелательно блокировать, всё же его блокировка возможна и менее заметна, чем блокировка ГУИ.

"Потребитель" и "управляющий" находятся в разных потоках, и если предполагается использовать контейнер событий именно в "управляющем", то нужно либо неблокирующее событие (QCoreApplication::postEvent), либо неблокирующий сигнал (Qt::QueuedConnection).
Наверное, контейнер лучше оставить у "управляющего". Мне кажется, это более логично. ГУИ достаточно уметь только выводить окошко и сообщать результат. А обработка очереди сообщений об ошибках логичнее смотрится в управляющем потоке.

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


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