对有序非重入调用的简单断言?

我有两个功能: void prepare()和void finish()将按顺序调用,如:
prepare();
<do something>;
finish(); 
... 
prepare(); 
<do something>; 
finish();
我想做一个简单的断言来简单地测试它们实际上是以这种方式调用的,并且它们不是在应用程序中同时调用或无序调用。 此应用程序是单线程应用程序。这是一个简单的开发/测试健全性检查,以确保按顺序调用这些函数,并且无论出于何种原因,它们都不会被同时调用。此外,这些断言/健全性检查应该从生产代码中省略,因为性能至关重要! 一个简单的断言()这样的工作最好吗?
int test = 0;

void prepare() {
   assert(++test == 1);

   .
   .
   .
}

void finish() {
    assert(--test == 0);

    .
    .
    .
}
    
已邀请:
你的代码没问题,除非你需要允许嵌套
prepare
finish
来电。 如果不允许嵌套,可以使用
bool
而不是
int
bool locked = false;;

void prepare() {
    assert( ! locked );
    locked = true;
    ...
}

void finish() {
    assert( locked );
    locked = false;
    ...
}
    
你可能想要改变
int test = 0;
#ifndef NDEBUG
int test = 0;
#endif
满足您的要求“任何与此测试有关的代码都应从生产中省略”。     
你可能想要:
int test = 0;

void prepare() {
    // enter critical section
    assert(test++ == 0);

    .
    .
    .
    // leave critical section 
}

void finish() {
    // enter critical section
    assert(--test == 0);

    .
    .
    .
    // leave critical section
}
    
这里有一个竞争条件:two2ѭ的两个并发实例可能同时取
test
的值,然后在寄存器中递增它以获得
1
,然后进行比较得到
true
。 使它成为
volatile
并没有帮助。相反,你应该在
test
上放一个互斥量,如下所示:
boost::mutex mtx;
int test = 0;

void prepare()
{
    boost::mutex::scoped_try_lock lock(&mtx);
    assert(lock.owns_lock());
    assert(test++ == 0);
    // ...
}

void finish()
{
    boost::mutex::scoped_try_lock lock(&mtx);
    assert(lock.owns_lock());
    assert(--test == 0);
}
    
如果你将
<do something>;
放入
class
,你可以减少检查的需要: 只需要构造函数调用
prepare
和析构函数调用
finish
。然后它会自动强制执行它们被恰当地调用。 请注意,并发和嵌套问题仍然适用:如果您想要阻止嵌套,那么您仍然需要某种全局状态(静态类成员?)来跟踪它,如果它在多个线程中使用,则访问它计数器需要受互斥保护。 另请注意,您也可以私有
operator new/delete
以防止有人在堆上创建一个并且不会破坏它。     
既然您正在使用C ++,为什么不使用RAII?你仍然需要检查重入使用,但RAII大大简化了事情。结合larsmans的互斥体和Raedwald在NDEBUG中的消除:
struct Frobber {
  Frobber() {
    assert(mtx.try_lock());
#ifndef NDEBUG
    try {  // in case prepare throws
#endif
      prepare();
#ifndef NDEBUG
    }
    catch (...) {
      mtx.unlock();
      throw;
    }
#endif
  }

  void something();
  // And the other actions that can be performed between preparation and finishing.

  ~Frobber() {
    finish();
#ifndef NDEBUG
    mtx.unlock();
#endif
  }

private:
#ifndef NDEBUG
  static boost::mutex mtx;
#endif

  Frobber(Frobber const&);  // not defined; 0x: = delete
  Frobber& operator=(Frobber const&);  // not defined; 0x: = delete
};
#ifndef NDEBUG
boost::mutex Frobber::mtx;
#endif

void example() {
  Frobber blah;  // instead of prepare()
  blah.something();
  // implicit finish()
}
在内部示例中,如果没有事先准备就完全无法执行某些操作,即使抛出异常,也总会发生完成。 关于NDEBUG的附注:如果以这种方式使用它,请确保它在所有转换单元中始终定义或始终未定义,而不是它如何用于断言(允许在各个点定义和未定义)。     

要回复问题请先登录注册