-1

我正在尝试解决由于对正在被破坏的对象调用方法而导致我的应用程序崩溃的问题。我有以下课程:

// 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_ptrB 类,它将原始 ptr 存储到 A。B 使用 Timer 的单例实例,它在不同的线程中运行,它安排函数 B::Update() 每 1000 毫秒在该线程上执行一次。有时,当我销毁A作为成员变量存储在堆栈上的对象时,它A被销毁了,但是在 class 中有一个指向它的悬空指针B,这会导致从另一个线程调用的函数B::Update()使应用程序崩溃,因为 m_pA* 不是nullptr,所以这会导致未定义的行为。

我应该如何处理,以防止我的应用程序崩溃?将明确设置m_pA = nullptrB::~B()足够了吗?

4

2 回答 2

1

将显式设置 m_pA = nullptr; 在 B::~B() 中足够了吗?

不,这会让你以后头疼,因为你不仅会有竞争条件(考虑线程读取值,指针设置为空,对象被删除,然后线程尝试使用对象),您最终还将尝试从刚刚删除的对象中读取内存。

我应该如何处理,以防止我的应用程序崩溃?

确保 B 的生命周期比线程长(最简单的方法是使线程归 B 拥有)并确保在访问时(如果读/写)所有共享内存都受到保护。请记住,您不仅必须确保不会对已删除的数据调用 update();但是在调用更新时不要删除数据。

于 2017-12-11T17:01:31.810 回答
0

如果它在 内崩溃B::Update(),那么这告诉我它Timer::ref().Cancel()甚至会在它完全取消另一个线程中的计时器之前返回,包括计时器是否正在执行预定的函数。如果您可以更改Timer's 的Cancel()工作方式,或为其添加另一种方法,我会保证计时器确实为您的 ID 取消/完成。这样,销毁A将调用~B()它将取消计时器,并且如果它在中间,则永远不会调用Update()或至少在完成之前不会返回。Update()

但是假设你不能这样做,那么我唯一能想到的就是制作B一个std::shared_ptrin A,并将线程 astd::weak_ptr赋予那个B,并尝试shared_ptrweak_ptrTimer 的预定函数回调中获取它。B::Update()只有当对象处于活动状态时才会调用这种方式B,并且它会在Update(). 然后你的下一个问题是保持A活动,因为Update()调用m_pA->DoSomething();,这也可以通过B持有一个weak_ptrtoA并创建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类的用户现在必须将其创建为共享对象,这可能是不可取的。

于 2017-12-11T20:41:57.003 回答