假设我有一个在某些机器上全天候运行的库。即使代码坚如磐石,硬件故障也迟早会触发异常。对于像这样的事件,我想有一些故障保险。一种方法是编写封装每个api的包装函数:
returnCode=DEFAULT;
try
{
returnCode=libraryAPI1();
}
catch(...)
{
returnCode=BAD;
}
return returnCode;
然后,库的调用者重新启动整个线程,如果returnCode错误,则重新初始化模块。
事情可能会发生严重错误。 E.g。
如果try块(或libraryAPI1())具有:
func1();
char *x=malloc(1000);
func2();
如果func2()抛出异常,则永远不会释放x。同样,文件损坏也是可能的结果。
请告诉我在这种情况下可能出现的其他问题?
答案 0 :(得分:4)
此代码:
func1();
char *x=malloc(1000);
func2();
不是C ++代码。这就是人们用类称为C的东西。它是一种看起来像C ++的程序风格,但与现实生活中C ++的使用方式不相符。原因是;好的异常安全C ++代码实际上从不需要在代码中使用指针(直接),因为指针总是包含在专门设计用于在异常安全庄园中管理其生命周期的类中(通常是智能指针或容器)。
该代码的C ++等价物是:
func1();
std::vector<char> x(1000);
func2();
答案 1 :(得分:3)
硬件故障可能不会导致C ++异常。在某些系统上,硬件异常是一种与C ++异常完全不同的机制。在其他情况下,C ++异常建立在硬件异常机制之上。所以这不是一个普遍的设计问题。
如果您希望能够恢复,则需要进行事务处理 - 每个状态更改都需要运行完成或完全退出。 RAII就是其中的一部分。正如克里斯贝克在另一个答案中指出的那样,除了资源获取之外,还有更多要说明的内容。
有一个复制 - 修改 - 交换习惯用法,它大量用于交易,但如果你试图调整工作代码来处理这个百万分之一的情况,这可能会太沉重。
如果您真的需要健壮性,那么将代码隔离到一个过程中。如果硬件故障导致进程终止,您可以让看门狗重新启动它。操作系统将收回丢失的资源。您的代码只需要担心具有持久状态的事务,就像保存到文件中的东西一样。
答案 2 :(得分:2)
您是否可以控制libraryAPI实施?
如果它适合OO模型,则需要使用RAII模式进行设计,这样可以保证在异常时调用析构函数(将释放获取的资源)。
使用资源管理辅助工具(如智能指针)也有帮助
try
{
someNormalFunction();
cSmartPtr<BYTE> pBuf = malloc(1000);
someExceptionThrowingFunction();
}
catch(...)
{
// Do logging and other necessary actions
// but no cleaning required for <pBuf>
}
答案 3 :(得分:2)
exeptions的问题是 - 即使你使用RAiI重新设计 - 它的仍然容易使代码变得不同步:
void SomeClass::SomeMethod()
{
this->stateA++;
SomeOtherMethod();
this->stateB++;
}
现在,该示例可能看起来很人性化,但如果您将stateA ++和stateB ++替换为以某种方式更改类状态的操作,则此类的预期结果是状态保持同步。 RAII可能会在使用异常时解决与状态相关的一些问题,但它所做的只是提供一种错误的安全感 - 如果SomeOtherMethod()抛出异常,则需要分析所有周围的代码以确保发布条件(stateA。满足delta == stateB.delta。