我正在尝试解决由正在破坏的对象上调用方法导致我的应用程序崩溃而导致的问题。我有以下课程:
// Forward declarations
class A;
class B
{
public:
B(A* aPtr) : m_pA(aPtr) { Schedule(); }
~B() { Timer::ref().Cancel(m_TimerId); }
void Schedule();
void Update();
private:
A* m_pA;
int32_t m_TimerId;
}
void B::Schedule()
{
m_TimerId = Timer::ref().Schedule(1000, [this]() { Update(); });
}
void B::Update()
{
m_pA->DoSomething();
}
class A
{
public:
void Create() { m_B = std::make_unique<B>(); }
void DoSomething() {}
private:
std::unique_ptr<B> m_B;
}
基本上,类 A 将unique_ptr
保存到类 B,后者将原始 ptr 存储到 A 中.B 使用计时器的单例实例,该实例在不同的线程中运行,并且它调度函数 B::Update() 每 1000 毫秒在该线程上执行一次。有时当我销毁对象时,该对象将A
存储为堆栈上的成员变量,A
被销毁,但是在类B
中有一个指向它的悬空指针,这会导致从另一个线程调用的函数B::Update()
使应用程序崩溃,因为 m_pA* 不是nullptr
,所以这会导致未定义的行为。
我应该如何处理这个问题,以防止我的应用程序崩溃?将明确设置m_pA = nullptr
;B::~B()
够吗?
显式设置 m_pA = nullptr; 在 B::~B() 中就足够了吗?
不。 这会让你以后头疼,因为你不仅仍然会有争用条件(考虑线程读取值的情况,指针设置为 null,对象被删除,然后线程尝试使用该对象),你最终还会尝试从你刚刚删除的对象中读取内存。
我应该如何处理这个问题,以防止我的应用程序崩溃?
确保 B 的生存时间比线程长(最简单的方法是使线程归 B 所有),并确保所有共享内存在访问时都受到保护(如果是读/写)。 请记住,您不仅必须确保不会对已删除的数据调用 update();但是,在调用更新时不会删除数据。
如果它在B::Update()
内崩溃,那么这告诉我Timer::ref().Cancel()
甚至在它完全取消另一个线程中的计时器之前就会返回,包括计时器是否正在执行调度函数。如果您可以更改Timer
的Cancel()
的工作方式,或向其添加其他方法,我会保证计时器真正为您的 id 取消/完成。这样,销毁A
将调用~B()
这将取消计时器,并且永远不会调用Update()
或者至少在Update()
完成之前不会返回(如果它处于中间)。
但是假设你不能这样做,那么我唯一能想到的就是B
A
std::shared_ptr
,并给线程一个std::weak_ptr
到那个B
,并尝试从计时器的调度函数回调中的weak_ptr
获取其shared_ptr
。这样,只有当B
对象处于活动状态时,才会调用B::Update()
,并且它将在Update()
期间使其保持活动状态。然后你的下一个问题是保持A
活动状态,因为Update()
调用m_pA->DoSomething();
,这也可以通过B
持有一个weak_ptr
来A
,并A
成为一个共享的指针对象来处理。但是,任何这些问题都在于,您在B
的构造上启动计时器,并且在C++17之前,您无法在构造期间从共享对象获取std::weak_ptr
。所以你必须移动Schedule()
才能被A
调用。
所以像这样:
class A;
class B : public std::enable_shared_from_this<B>
{
public:
B() = default;
~B() { Timer::ref().Cancel(m_TimerId); }
void Schedule(const std::shared_ptr<A>& aPtr);
void Update();
private:
std::weak_ptr<B> weak_from_this()
{
return shared_from_this();
}
private:
std::weak_ptr<A> wkA;
int32_t m_TimerId{0};
};
void B::Schedule(const std::shared_ptr<A>& aPtr)
{
wkA = aPtr;
m_TimerId = Timer::ref().Schedule(1000,
[myself = weak_from_this()]()
{
if (auto me = myself.lock())
{
me->Update();
}
});
}
void B::Update()
{
if (auto pA = wkA.lock())
{
pA->DoSomething();
}
}
class A : public std::enable_shared_from_this<A>
{
public:
void Create()
{
m_B = std::make_shared<B>();
m_B->Schedule(shared_from_this());
}
void DoSomething() {}
private:
std::shared_ptr<B> m_B;
};
请注意,上面做了一些直到 C++17 才需要的事情。此外,这意味着A
类的用户现在必须将其创建为共享对象,这可能是不可取的。