在非 C++ 异常的情况下解锁临界区

Posted

技术标签:

【中文标题】在非 C++ 异常的情况下解锁临界区【英文标题】:Unlocking the critical section in case of non-C++ exceptions 【发布时间】:2009-04-29 14:31:20 【问题描述】:

我的类中有一个 CCriticalSection 对象,用于将异常同步到方法。我将它与 CSingleLock 对象一起使用,如下所示:

void f()

  try
   
     CSingleLock lock(&m_synchronizer, TRUE);
     .....
     .....

   

  catch(SomeException )
  
  

  catch(...)
  
  


如果任何语句引发 C++ 异常,则临界区对象已正确解锁,但是如果我遇到任何其他类型的异常(例如访问冲突),有什么方法可以解锁我的临界区吗?我认为 RAII 在这里不会有帮助,因为堆栈展开不会发生。在退出函数 f 后,我可以做些什么来防止关键部分处于锁定状态?

【问题讨论】:

【参考方案1】:

编辑更新

CSingleLock 的析构函数确实会解锁底层的临界区。访问冲突等称为 SEH 异常。至于析构函数是否会在遇到 SEH 异常时运行是非常特定于环境的。您可以采取一些技巧来使这种类型的场景发挥作用。

例如,一种选择是将所有 SEH 异常转换为相应的 C++ 异常。我最近写了一篇关于实现这一点的技术的博客。一旦它们都是 C++ 异常,则保证调用的析构函数。

http://blogs.msdn.com/jaredpar/archive/2008/01/11/mixing-seh-and-c-exceptions.aspx

但另一个问题是为什么要这样做呢?一旦您遇到访问冲突,您的程序唯一可以可靠地做的就是崩溃。

【讨论】:

即使 CSingleLock 也能做到。不是吗? @Naveen,正在调查。我对那个类不熟悉(我花了更多时间在 ATL 版本上)。 看起来是一篇不错的博客文章 - 稍后我将不得不更仔细地阅读它。但是,捕获和处理 SEH 异常可能很有用,尤其是对于验证参数(尤其是在处理从不受信任的代码(例如插件)调用时)。在这种情况下,您可以返回错误而不是崩溃,并且可以可靠地执行此操作。至少相当可靠。 非常好的文章@JaredPar。 +1。 不错的博客@JaredPar,很高兴知道这个问题的 4 个答案中至少有 2 个是通过 Dvorak 输入的。与权力战斗!【参考方案2】:

假设您使用的是 MSVC,请尝试使用 /EHa 命令行选项进行编译,该选项将 Windows 的结构化异常映射到 C++ 异常(C++ 文档有时称它们为“异步”异常)。

您应该阅读此选项的含义 - 我自己不使用该选项,而且我不确定可能有什么缺点。

您也可以尝试使用__try__except__finally 关键字自己处理 SEH。

【讨论】:

如果你使用 __try 等,它不会让你在该函数中创建任何具有非平凡析构函数的对象 啊 - 那么您要么使用 /EHa,要么对代码进行大量重组,要么使用 JaredPar 在他的博客上写的东西。【参考方案3】:

RAII 在这里确实有帮助。不在这里最重要的是“超出范围”不一定只是堆栈展开。当 try 块退出时,无论是正常退出还是通过异常退出,都会调用析构函数。每当您的变量超出范围时,都会调用其析构函数。

编辑

嗯,问题似乎已被编辑为关注 SEH(非 C++ 异常)。在这种情况下,析构函数代码可能不会被触发。但是,正如其他人所指出的,大多数非 C++ 异常都等同于应用程序崩溃。除了崩溃之外,以任何理智的方式对这些异常做出反应可能是不合理的。在这种情况下,您的应用程序将退出,并且您的关键部分将被销毁。

有关将 C SEH 异常转换为 Windows 的 C++ 异常的良好解决方案,请参阅 @JaredPar 的回答。

【讨论】:

我认为在结构化异常的情况下不会调用锁对象的析构函数。 @Naveen,这取决于您的编译器设置。如果您启用 ASYNC 异常处理,它们将被调用。 @Naveen,C++ 异常是 SEH 异常。 catch 和 __catch 之间的唯一区别是前者只会捕获一部分案例。无论如何,为什么不测试呢? @Richard - C++ 异常不是结构化异常,尽管编译器可以选择将 SEH 异常转换为 C++ 异常。【参考方案4】:

如果您确实需要您的应用程序在特定区域的代码执行访问冲突时继续存在,您应该考虑将该代码放在单独的进程中。

如果代码试图读取或写入不允许的地方,有什么办法阻止它写入应用程序内存的其他部分,从而导致各种讨厌的问题?

【讨论】:

【参考方案5】:

AFAIK,你 NEVER 应该使用 CCriticalSection 或任何 MFC 同步对象。

【讨论】:

以上是关于在非 C++ 异常的情况下解锁临界区的主要内容,如果未能解决你的问题,请参考以下文章

关于C++临界区CriticalSection的问题。

创建线程安全单例 (C++) 时的临界区初始化

SIGABRT 在临界区模拟 C++ 代码

c++多线程问题

C++并发编程 - 同步并发操作

Openmp 程序在没有临界区的情况下工作