如何捕获在临界区中抛出的异常?

6

我正在使用C++进行Win32多线程编程。

场景:

我有一个被多个线程使用的函数。

这个函数包含一个关键区域(或者任何可以锁定资源的构造)。

在关键区域中抛出异常。

此时,我需要在异常捕获块中解除锁定资源。

是否有其他方法可以实现这一点?我的意思是,假设我不想在catch块中记得释放锁定,是否有常见的方法来处理这个问题,以避免这种容易出错的情况?

4个回答

16

这个想法是将获取和释放临界区的行为封装在一个对象中,这样构造该对象会获取临界区,销毁该对象会释放它。

struct CSHolder {
    explicit CSHolder(CRITICAL_SECTION& cs): lock(cs) {
        ::EnterCriticalSection(&lock);
    }
    ~CSHolder() { ::LeaveCriticalSection(&lock); }
    CRITICAL_SECTION& lock;
};


CRITICAL_SECTION gLock;
void foo() {
    CSHolder lockIt(gLock);
    // lock is held until lockIt is destroyed
}

这个概念被称为RAII - 资源获取即初始化。它是现代C++中非常常见的习语。


3
如果您正在使用现有的框架,您很可能已经拥有一个可以为您完成此操作的 RAII 容器类。如果您正在使用 MFC,请查看 CSingleLock,如果您正在使用 boost,请查看 scoped_lock
可惜的是,似乎每个人都必须(或者认为他们必须)自己编写这个类。

我同意...令人遗憾的是,人们仍在直接使用CRITICAL_SECTION而不是使用boost::mutex或类似的东西。也许正确的答案是编写一个适配器,在本机CS上实现boost Lockable概念..嗯... - D.Shawley

2
如果您可以使用MFC,那么您可以使用CSingleLock来完成此操作。您可以像这样使用它:
void f()
{
  try
  {
    CSingleLock lock(&m_criticalSection, TRUE);

  }
  catch(/*some exception*/)
}

这段文字的意思是:锁将在其析构函数中负责解锁临界区。由于锁是一个局部对象,当抛出异常时,堆栈展开并执行锁对象的析构函数,从而解锁您的临界区。

2

编写一个括号类,该类将关键部分作为构造函数参数。在构造函数中调用EnterCriticalSection,在析构函数中调用LeaveCriticalSection。如果抛出C++异常,堆栈展开将完成其余工作。


这是boost::mutex::lock对象的行为。 - greyfade

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接