С++: как реализовать тайм-аут для произвольного вызова функции?

Мне нужно вызвать библиотечную функцию, которая иногда не завершается в течение заданного времени, к сожалению. Есть ли способ вызвать функцию, но прервать ее, если она не завершается в n секунд?

Я не могу изменить функцию, поэтому я не могу поместить в нее условие abort напрямую. Я должен добавить тайм-аут к функции извне.

возможно ли, что это возможное решение запустить его как (boost) поток, который я могу завершить после в определенное время? Будет что-то подобное? Я действительно считаю, что функция не потокобезопасный, но это не имело бы значения, если бы я запускал его как только один поток, верно? Есть ли другие (лучшие) решения?

9 ответов


вы можете породить boost::thread для вызова API:

boost::thread api_caller(::api_function, arg1, arg2);
if (api_caller.timed_join(boost::posix_time::milliseconds(500)))
{
    // API call returned within 500ms
}
else
{
    // API call timed out
}

Boost не позволяет вам убить рабочий поток. В этом примере он просто осиротел.

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


Я думаю, что единственный безопасный способ сделать это-создать отдельный песочница процесс, который вызывает функцию библиотеки в качестве прокси-сервера для вашего приложения. Вам нужно будет реализовать некоторый тип IPC между вашим приложением и прокси-сервером. Реализация тайм-аута при чтении ответа IPC тогда довольно тривиальна. Если чтение не удается из-за таймаута, вы можете безопасно завершить прокси-сервер, не рискуя здоровьем вашего приложения.


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


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


импульс.Execution_monitor теста делает то, что вы хотите:

http://www.boost.org/doc/libs/1_39_0/libs/test/doc/html/execution-monitor/reference.html#boost.execution_monitor


проблема в том, что в процессе решения без поддержки функции вы в конечном итоге с потенциально недопустимое состояние.

пример: когда вы завершаете поток во время выделения памяти, ваша куча процессов может быть повреждена.

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

вы можете, как предлагает Бен Штрауб, просто осиротеть поток: поставить его на самый низкий приоритет и позволить ему работать до бесконечности. Это, конечно, только ограниченное решение: если поток потребляет ресурсы (вероятно), они замедлят систему, также есть ограничение на потоки на процесс (обычно из-за адресного пространства для стека потоков).

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

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


Что вам нужно, это резьба и Будущего Объекта это может содержать результат от вызова функции.

пример использования boost см. В разделе здесь.

вам нужно будет проверить будущее после тайм-аута, и если он не установлен, действуйте соответственно.


перейти с процессом сироты, запустить его и время его выполнения. Если у него заканчивается время, вызовите ОС, чтобы убить его.

Как избежать гонки conds. по этой схеме:

  • создайте файл для хранения в args (конечно, все передается как VALs). Процесс orphan разрешен только для чтения данных из этого файла.

  • сирота обрабатывает входные данные, создает выходной файл со значениями результата и закрывает он.

  • только когда все сделано, orphan удаляет входной файл, факт, который сигнализирует главному процессу, что работа была сделана.

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


" Мне нужно вызвать библиотечную функцию, которая, к сожалению, иногда не завершается в течение заданного времени. Есть ли способ вызвать функцию, но прервать ее, если она не завершается в течение n секунд?"

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

Так, по возможности старайтесь, чтобы код, запущенный в потоке, при необходимости выходил чисто-сам код должен обнаружить и обработать ошибку. Он может отправить сообщение и / или установить статусы, чтобы основной (или другой) поток знал, что произошло.

личные предпочтения, в высокодоступных системах, мне нравится, что мои потоки вращаются часто (без блокировки занятости) с определенными тайм-аутами, вызывая неблокирующие функции и с точными условиями выхода на месте. Глобальное или потоковое "готово" переменная делает трюк для чистого выхода.