不幸的是,我需要调用一个有时不会在给定时间内终止的库函数。有没有办法调用该函数但如果它没有在n
几秒钟内终止则中止它?
我不能修改函数,所以我不能直接把中止条件放进去。我必须在外部为函数添加超时。
将它作为(增强)线程启动是否可能是一种可能的解决方案,然后我可以在一段时间后终止它?这样的东西会起作用吗?我实际上相信该函数不是线程安全的,但是如果我将它作为唯一的单线程运行,那也没关系,对吧?还有其他(更好的)解决方案吗?
您可以生成一个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 回复时实现超时是相当简单的。如果读取由于超时而失败,您可以安全地终止代理,而不会危及应用程序的健康。
您所说的通常称为“看门狗”系统。看门狗通常是检查所有其他线程状态的第二个线程。看门狗通常设置为定期运行。如果没有收到来自其他线程的响应,则看门狗可以通知用户,甚至在可能安全的情况下杀死有问题的线程(取决于您的应用程序)。
线程的问题是线程终止后您将无法释放某些资源。如果您没有获得必须释放的资源,则使用线程。
Boost.Test 的 execution_monitor 做你想做的事:
问题在于,如果使用没有函数支持的进程内解决方案,您最终可能会处于无效状态。
示例:当您在进行内存分配时终止线程时,您的进程堆可能已损坏。
因此,您可能会终止呼叫,但您也必须终止该过程。在许多情况下,破坏性副作用的可能性很小,但我不会把我的计算押在这上面。
正如 Ben Straub 建议的那样,您可以将线程孤立起来:将其置于最低优先级并让它无限运行。这当然只是一个有限的解决方案:如果线程消耗资源(可能),它们会减慢系统速度,每个进程的线程也有限制(通常是由于线程堆栈的地址空间)。
一般来说,我更喜欢外部流程解决方案。一个简单的模式是:
将输入数据写入文件,以文件作为参数启动外部进程。外部进程将进度(如果有的话)写入可以监控的磁盘文件,甚至可以允许进程从它开始的地方恢复。结果被写入磁盘,父进程可以读取它们。
当您终止进程时,您仍然必须处理对外部资源(如文件)的同步访问,以及如何处理废弃的 mutice、半写文件等。但这通常是获得强大解决方案的方法。
“不幸的是,我需要调用一个有时不会在给定时间内终止的库函数。有没有办法调用该函数,但如果它没有在 n 秒内终止,则中止它?”
最简洁的答案是不。这通常很麻烦...调用本身必须在某个时间终止(实现自己的超时),但阻塞调用通常很麻烦(例如 gethostbyname()),因为这取决于它们(或系统)的超时,而不是您的超时。
因此,只要有可能,就尽量让线程中运行的代码在必要时干净地退出——代码本身必须检测并处理错误。它可以发送消息和/或设置状态,以便主(或其他)线程知道发生了什么。
个人偏好,在高可用性系统中,我喜欢我的线程经常旋转(虽然没有忙锁定),有特定的超时,调用非阻塞函数,并且有精确的退出条件。全局或线程特定的“完成”变量可以实现干净退出。
使用孤儿进程,启动它并确定其执行时间。如果超时,请调用操作系统将其杀死。
如何避免比赛条件。在这个模式上:
创建一个文件以存储在 args 中(当然,所有内容都作为 VAL 传递)。孤儿进程只允许从此文件中读取数据。
孤儿处理输入数据,创建带有结果值的输出文件并关闭它。
只有当一切都完成后,orphan 才会删除输入文件,这是向主进程发出工作已完成的信号。
这避免了读取半写文件的问题,因为master首先注意到输入文件的缺失,打开读取输出文件,这肯定是完成的(因为在删除输入之前关闭,并且操作系统调用堆栈是顺序的)。