Подтвердить что ты не робот

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

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

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

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

4b9b3361

Ответ 1

Вы можете вызвать 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, потому что он никогда не сможет освободить ресурсы, которые он приобрел.

Ответ 2

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

Ответ 3

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

Ответ 4

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

Ответ 6

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

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

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

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

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

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

Ответ 7

Вам нужен поток и Future Object, который может удерживать результат вызова функции.

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

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

Ответ 8

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

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

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

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

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

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

Ответ 9

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

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

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

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