4

假设我有一个在某些机器上运行 24x7 的库。即使代码坚如磐石,硬件故障也迟早会触发异常。我想为这样的事件设置某种故障保护装置。一种方法是编写封装每个 api a 的包装函数:

returnCode=DEFAULT;
try
{
  returnCode=libraryAPI1();
 }
catch(...)
{
 returnCode=BAD;
}
return returnCode;

然后库的调用者重新启动整个线程,如果 returnCode 错误则重新初始化模块。

事情可能会大错特错。例如

如果 try 块(或 libraryAPI1())有:

 func1();
 char *x=malloc(1000);
 func2();

如果 func2() 抛出异常,x 将永远不会被释放。同样,文件损坏也是一种可能的结果。

你能告诉我在这种情况下还有什么其他可能出错的地方吗?

4

4 回答 4

4

这段代码:

func1();
char *x=malloc(1000);
func2();

不是 C++ 代码。这就是人们所说的带有类的 C。它是一种看起来像 C++ 但与 C++ 在现实生活中的使用方式不匹配的程序风格。原因是; 良好的异常安全 C++ 代码实际上从不需要在代码中(直接)使用指针,因为指针始终包含在一个专门设计用于在异常安全庄园(通常是智能指针或容器)中管理其生命周期的类中。

该代码的 C++ 等效项是:

func1();
std::vector<char> x(1000);
func2();
于 2010-05-20T16:38:41.433 回答
3

硬件故障可能不会导致 C++ 异常。在某些系统上,硬件异常是与 C++ 异常完全不同的机制。在其他方面,C++ 异常建立在硬件异常机制之上。所以这不是一个真正的一般设计问题。

如果您希望能够恢复,您需要是事务性的——每个状态更改都需要运行到完成或完全退出。RAII 就是其中的一部分。正如 Chris Becke 在另一个答案中指出的那样,除了资源获取之外,还有更多需要说明的内容。

有一个复制修改交换习惯用于交易,但如果你试图调整工作代码来处理这个百万分之一的情况,这可能太重了。

如果你真的需要健壮性,那么将代码隔离到一个进程中。如果硬件故障导致进程终止,您可以让看门狗重新启动它。操作系统将回收丢失的资源。您的代码只需要担心具有持久状态的事务性,例如保存到文件的内容。

于 2010-05-20T17:09:54.997 回答
2

例外的问题是——即使你用 RAiI 重新设计——它仍然很容易使代码变得不同步:

void SomeClass::SomeMethod()
{
  this->stateA++;
  SomeOtherMethod();
  this->stateB++;
}

现在,这个例子可能看起来很假,但是如果你用 stateA++ 和 stateB++ 代替以某种方式改变类状态的操作,这个类的预期结果是状态保持同步。RAII 可能会在使用异常时解决与状态相关的一些问题,但它所做的只是提供一种错误的安全感 - 如果 SomeOtherMethod() 抛出异常,则需要分析所有周围的代码以确保后置条件(stateA. delta == stateB.delta) 得到满足。

于 2010-05-20T08:06:03.033 回答
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>
}
于 2010-05-20T03:37:46.397 回答