gpt4 book ai didi

c++ - 在 C++ 中,如何防止函数被递归调用

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:48:34 25 4
gpt4 key购买 nike

我有一个使用堆上内存的函数,如果在同一函数的另一个实例完成之前调用它,它将出现严重错误。我怎样才能防止这种情况在编译时发生?

最佳答案

在编译时用任何数量的确定性来检测递归将是相当困难的。一些静态代码分析工具可能能够做到这一点,但即便如此,您也可能会遇到代码分析器无法检测到的涉及线程的运行时场景。

您需要在运行时检测递归。从根本上说,这样做非常简单:

bool MyFnSimple()
{
static bool entered = false;
if( entered )
{
cout << "Re-entered function!" << endl;
return false;
}
entered = true;

// ...

entered = false;
return true;
}

最大的问题当然是它不是线程安全的。有几种方法可以使其成为线程安全的,最简单的方法是使用临界区并阻止第二个条目,直到第一个条目离开。 Windows 代码(不包括错误处理):

bool MyFnCritSecBlocking()
{
static HANDLE cs = CreateMutex(0, 0, 0);
WaitForSingleObject(cs, INFINITE);
// ... do stuff
ReleaseMutex(cs);
return true;
}

如果你想让函数在重新进入函数时返回错误,你可以先测试一下critsec再抓取:

bool MyFnCritSecNonBlocking()
{
static HANDLE cs = CreateMutex(0, 0, 0);
DWORD ret = WaitForSingleObject(cs, 0);
if( WAIT_TIMEOUT == ret )
return false; // someone's already in here
// ... do stuff
ReleaseMutex(cs);
return true;
}

除了使用静态 bool 和 critsecs 之外,可能有无数种方法可以给这只猫蒙皮。我想到的是将本地值与 Windows 中的一个互锁函数结合使用:

bool MyFnInterlocked()
{
static LONG volatile entered = 0;
LONG ret = InterlockedCompareExchange(&entered, 1, 0);
if( ret == 1 )
return false; // someone's already in here
// ... do stuff
InterlockedExchange(&entered, 0);
return false;
}

当然,您必须考虑异常安全和死锁。您不希望函数中的失败导致任何代码都无法输入它。您可以将上述任何结构包装在 RAII 中以确保在您的函数发生异常或提前退出时释放锁。

更新:

阅读评论后,我意识到我可以包含说明如何实现 RAII 解决方案的代码,因为您编写的任何实际代码都将使用 RAII 来处理错误。这是一个简单的 RAII 实现,它还说明了出现问题时在运行时会发生什么:

#include <windows.h>
#include <cstdlib>
#include <stdexcept>
#include <iostream>

class CritSecLock
{
public:
CritSecLock(HANDLE cs) : cs_(cs)
{
DWORD ret = WaitForSingleObject(cs_, INFINITE);
if( ret != WAIT_OBJECT_0 )
throw std::runtime_error("Unable To Acquire Mutex");
std::cout << "Locked" << std::endl;
}
~CritSecLock()
{
std::cout << "Unlocked" << std::endl;
ReleaseMutex(cs_);
}
private:
HANDLE cs_;
};

bool MyFnPrimitiveRAII()
{
static HANDLE cs = CreateMutex(0, 0, 0);
try
{
CritSecLock lock(cs);
// ... do stuff
throw std::runtime_error("kerflewy!");
return true;
}
catch(...)
{
// something went wrong
// either with the CritSecLock instantiation
// or with the 'do stuff' code
std::cout << "ErrorDetected" << std::endl;
return false;
}
}

int main()
{
MyFnPrimitiveRAII();
return 0;
}

关于c++ - 在 C++ 中,如何防止函数被递归调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2201012/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com