2010-11-20 4 views
3

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

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

Вот как я хотел бы сделать это с напряженным циклом ожидания, в псевдокоде:

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

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

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

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

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

ответ

1

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

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

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

+0

Спасибо! Я думаю, это звучит так, как хотелось бы, но у меня недостаточно информации, чтобы понять, как это сделать. В частности, я не знаю, как правильно установить сигнал, который прервет блокирующий вызов и навсегда остановит его, а не вернется к нему после выхода из обработчика прерываний. Не могли бы вы добавить немного объяснений о том, как это сделать? –

+1

Из ссылки GNU в моем ответе: «Чтобы иметь возможность использовать функцию тревоги для прерывания системного вызова, который может блокироваться иначе неопределенно, важно не устанавливать флаг SA_RESTART при регистрации обработчика сигнала с использованием sigaction». Я бы попробовал это. Тогда возникает вопрос, что делает код библиотеки, когда его примитивные операции возвращают EINTR. Если вам повезет, библиотека, которую вы используете, будет иметь некоторую функцию «остановки», которую вы можете вызывать из обработчика сигнала, или она просто вернется из блокирующего вызова в EINTR. Если он более настойчив и снова блокирует EINTR, жизнь становится сложнее. –

+0

Хорошо, это начинает иметь смысл - я пропустил эту линию, а также не понял, что существует SA_RESTART, или что-либо может случиться с прерванным кодом, кроме того, что он продолжается, когда он остановился. Спасибо что подметил это! (Я не уверен, что делает код библиотеки в этом случае или насколько мы можем полагаться на это, так как у него есть крючки для людей, чтобы добавить другой код на этом уровне для разных коммуникационных библиотек, но я узнаю.) –

1

Что вы хотите, это что-то вроде select(2), в зависимости от того, на какую ориентацию вы нацеливаете.

+0

Что-то вроде, да. Это, похоже, специфично для дескрипторов файлов, хотя, хотя это и происходит с произвольным вызовом библиотеки. Он может или не может использовать файловые дескрипторы глубоко под ним, в зависимости от используемой версии, но у меня нет доступа к ним. Спасибо хоть! –

1

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

1

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

1

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

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

+0

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

Смежные вопросы