Как реализовать синхронизированное ожидание вокруг блокирующего вызова? - PullRequest
3 голосов
/ 20 ноября 2010

Итак, ситуация такая. У меня есть библиотека C ++, которая выполняет межпроцессное взаимодействие с функцией wait(), которая блокирует и ожидает входящее сообщение. Сложность в том, что мне нужно timed wait, которое вернется со значением статуса, если за указанный промежуток времени сообщение не получено.

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

Вот как я могу сделать это с занятым циклом ожидания в псевдокоде:

while(message == false && current_time - start_time < timeout)
{
  if (Listener.new_message()) then message = true;
}

Я не хочу занятого ожидания, которое съедает циклы процессора. И я также не хочу просто добавлять в цикл вызов sleep(), чтобы избежать загрузки процессора, поскольку это означает более медленный ответ. Я хочу что-то, что делает это с правильным видом блоков и прерываний. Если лучшее решение предполагает многопоточность (что кажется вероятным), мы уже используем boost::thread, поэтому я бы предпочел использовать это.

Я отправляю этот вопрос, потому что это похоже на ситуацию, в которой был бы ясный правильный ответ "лучшие практики", поскольку это довольно распространенная модель. Как правильно это сделать?

Изменить, чтобы добавить: Большая часть моей озабоченности здесь заключается в том, что это место в программе, которое является критичным по производительности и критичным, чтобы избежать условий гонки или утечек памяти. Таким образом, хотя «использовать два потока и таймер» является полезным советом, я все еще пытаюсь выяснить, как на самом деле реализовать это безопасным и правильным способом, и я легко могу видеть, как я делаю ошибки новичка в коде, который я даже не знаю, что я сделал. Таким образом, некоторые примеры кода были бы очень полезны!

Кроме того, меня беспокоит многопотоковое решение: если я использую метод «поместить блокирующий вызов во второй поток и выполнить временное ожидание в этом потоке», что произойдет с этим вторым потоком, если заблокирован звонок никогда не возвращается? Я знаю, что время ожидания в первом потоке вернется, и я увижу, что ответа не последовало, и я продолжу с вещами, но разве я потом «просочился» в поток, который будет вечно сидеть в заблокированном состоянии? Есть ли способ избежать этого? (Есть ли способ избежать этого и избежать утечки памяти второго потока?) Полное решение того, что мне нужно, должно было бы избежать утечек, если блокирующий вызов не возвращается.

Ответы [ 6 ]

1 голос
/ 20 ноября 2010

Вы можете использовать sigaction(2) и alarm(2), которые оба являются POSIX.Вы устанавливаете действие обратного вызова для тайм-аута с помощью sigaction, затем вы устанавливаете таймер с помощью будильника, а затем делаете свой блокирующий вызов.Блокирующий вызов будет прерван, если он не завершится в течение выбранного вами тайм-аута (в секундах; если вам нужна более тонкая детализация, вы можете использовать setitimer(2)).

Обратите внимание, что сигналы в C несколько волосатые,довольно обременительное ограничение на то, что вы можете делать в обработчике сигналов.

Эта страница полезна и довольно кратка: http://www.gnu.org/s/libc/manual/html_node/Setting-an-Alarm.html

1 голос
/ 20 ноября 2010

Инкапсулировать блокирующий вызов в отдельном потоке. Имейте промежуточный буфер сообщений в том потоке, который защищен переменной условия (как сказано ранее). Сделайте ваш основной поток рассчитанным на эту переменную условия. Получите промежуточно сохраненное сообщение, если условие выполнено.

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

1 голос
/ 20 ноября 2010

Возможно, вы захотите взглянуть на синхронизированные блокировки : ваш метод блокировки может захватить блокировку, прежде чем начать ждать и отпустить ее, как только будут получены данные. Затем вы можете попытаться получить блокировку (с таймаутом) в вашем методе ожидания по времени.

1 голос
/ 20 ноября 2010

Похоже, вам нужен «монитор», способный сигнализировать о доступности ресурса для потоков через общий мьютекс (обычно). В Boost.Thread condition_variable может выполнить эту работу.

1 голос
/ 20 ноября 2010

То, что вы хотите, это что-то вроде select (2) , в зависимости от целевой ОС.

0 голосов
/ 28 марта 2018

Относительно

что происходит с этим вторым потоком, если заблокированный вызов никогда не возвращается?

Я считаю, что ничего вы не можете сделатьвосстановить чисто без взаимодействия с вызываемой функцией (или библиотекой).«Чисто» означает очистку всех ресурсов, принадлежащих этому потоку, включая память, другие потоки, блокировки, файлы, блокировки файлов, сокетов, ресурсы графического процессора ... Un - чисто, вы действительно можете убить побегнить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...