哪种代码被认为是异常安全的?

时间:2012-03-12 10:32:38

标签: c++ exception exception-handling pthreads mutex

处理异常的代码称为异常安全代码?这是对的吗?

从这里开始:https://codereview.stackexchange.com/a/9759/11619

  

您为互斥锁使用锁定/解锁对。这不是例外。   所以我会创建一个将在构造函数中执行锁定的对象   并在析构函数中解锁然后使用它来锁定你的互斥锁。这个   将使你的代码更加安全。

class MutexLocker
{
    pthread_mutex_t&  mutex;
    MutextLocker(pthread_mutex_t& mutex)
        : mutex(mutex)
    {
        pthread_mutex_lock(&mutex);
    }
    ~MutexLocker()
    {
        pthread_mutex_unlock(&mutex);
    }
};

以上显示的代码异常以哪种方式安全?我没有在那里看到任何异常处理。

或者异常安全代码是指我们可以“添加”异常处理的地方吗?因此,通过添加异常处理可以使上面显示的代码异常安全,但现在不是吗?

6 个答案:

答案 0 :(得分:6)

异常安全不是关于处理异常,而是即使在存在例外情况下也要保证程序的许多属性。

您通常可以谈论给定方法的exception safety level

  • 不保证:这种方法是不安全的例外,根本不保证。
  • 基本异常保证:如果异常没有资源泄露且涉及的类仍然可用(但是它们的状态未指定),那就是没有内存泄漏,没有文件句柄泄漏,没有互斥锁泄漏和相关实例的不变量仍然得到验证。
  • 强异常保证:如果发生异常,所有状态都会逆转到开始之前的状态。这是一个像语义一样的交易。
  • NoThrow Guarantee :此方法不会抛出,它总是成功。

通常, NoThrow保证仅适用于最简单的方法(例如.size()上的vector)和强例外保证实施成本可能很高(能够恢复效果或对国家副本进行操作可能并不便宜)。

另一方面,基本例外保证只是:基本。没有它,安全地运行程序是不可能的,所以这是最少保证是可以接受的。如果泄漏资源或使类处于不可用状态,则程序可能无法进一步运行。

这就是为什么每当提到例外情况时都会强调RAII。因为RAII保证资源(内存,互斥体,文件)的自动清理,无论路径执行(常规返回或异常),都是特别需要的。但是,RAII本身还不够。

相关:Herb Sutter关于Exception Safety and Exception Specifications的新闻。

答案 1 :(得分:3)

总是调用MutexLocker析构函数,也就是在构造它的块中引发异常时。

这使得使用MutexLocker异常的构造安全。

答案 2 :(得分:3)

您不必处理异常是异常安全的。你只需要在抛出异常时生存。

MutexLocker可帮助您在离开示波器时解锁互斥锁。如果您通过return语句离开或抛出异常并不重要。

答案 3 :(得分:2)

“异常安全”是一个相当重载的术语,但我会用它来描述可以通过它们抛出异常的代码部分,并且仍然保持某些不变量(例如 - 没有任何变化,没有资源被泄露,所有对象保持不变一个有效的州。)

例如,您的void * printHello (void* threadId)函数是正确的(因为它始终与pthread_mutex_lock (&demoMutex)匹配pthread_mutex_unlock (&demoMutex)),但如果有人更改了中间的部分,那么它可以抛出一个异常(例如,通过在std::cout上设置throw标志),然后您的代码将永久锁定demoMutex,并且没有希望它被释放。这将构成您程序中的错误。 void * printHello (void* threadId)被称为“异常不安全”,因为通过向看似无关的部分添加异常处理,可以很容易地将错误引入程序中。

使用RAII类来管理资源是编写异常安全代码的一种好方法(并且 资源管理风格在C ++中使用),因为它避免了重复手动清理的需要catch(...)阻止,并通过使用类型系统强制清理资源。

答案 4 :(得分:1)

  

处理异常的代码称为异常安全代码?

是的,但你没说好。 This是关于异常安全的好读物。

  

以上显示的代码异常以哪种方式安全?

该代码本身并非异常安全。这意味着要在异常安全的代码中使用。例如:

void bar()
{
  throw std::runtime_error( "lala" );
}

void foo()
{
  pthread_mutex_lock(&mutex);

  bar();

  // OPS! mutex not unlocked
  pthread_mutex_unlock(&mutex);
}

使用自动解锁异常的类可以解决此问题。下一个示例具有强有力保证的异常处理:

void foo()
{
  MutexLocker locker( mutex );

  try {
    bar();
  } catch ( const std::runtime_error & )
  {
    // revert changes
  }
}

答案 5 :(得分:0)

您提供的代码很有用。简单地说,当执行离开定义MutexLocker对象的块时,对象将被销毁,并且根据其析构函数释放互斥锁。无论出口的原因如何,这都有效。

你不必自己编写这门课程。 C ++标准指定了一个名为lock_guard的类,它正是这样做的。

http://en.cppreference.com/w/cpp/thread/lock_guard