C++ 如何防止截止时间计时器调用已删除类中的函数?

C++ 如何防止截止时间计时器调用已删除类中的函数?,c++,boost-asio,C++,Boost Asio,我在一段实际代码中遇到了问题,其中属于已删除类的函数由boost::asio::daildate\u计时器调用,有时会导致分段错误 我遇到的问题是,删除截止时间计时器是从同一io_服务上的另一个计时器运行的。删除第一个deadline\u计时器将触发对要运行的函数的最后一次调用,并出现boost::asio::error::operation\u中止错误。但是,这只能在删除完成后安排在(相同的)io_服务上,但此时该对象已被删除,因此不再有效 所以我的问题是:我怎样才能防止这种情况发生 以下是具

我在一段实际代码中遇到了问题,其中属于已删除类的函数由
boost::asio::daildate\u计时器调用,有时会导致分段错误

我遇到的问题是,删除截止时间计时器是从同一io_服务上的另一个计时器运行的。删除第一个
deadline\u计时器
将触发对要运行的函数的最后一次调用,并出现
boost::asio::error::operation\u中止
错误。但是,这只能在删除完成后安排在(相同的)
io_服务上
,但此时该对象已被删除,因此不再有效

所以我的问题是:我怎样才能防止这种情况发生

以下是具有相同故障的简化示例:

//============================================================================
// Name        : aTimeToKill.cpp
// Author      : Pelle
// Description : Delete an object using a timer, from a timer
//============================================================================

#include <iostream>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>

using namespace std;
using namespace boost;

struct TimeBomb
{
    bool m_active;
    asio::deadline_timer m_runTimer;

    TimeBomb(boost::asio::io_service& ioService)
    : m_active(true)
    , m_runTimer(ioService)
    {
        cout << "Bomb placed @"<< hex << (int)this << endl;
        m_runTimer.expires_from_now(boost::posix_time::millisec(1000));
        m_runTimer.async_wait(boost::bind(&TimeBomb::executeStepFunction, this, _1));
    }

    ~TimeBomb()
    {
        m_active = false;
        m_runTimer.cancel();
        cout << "Bomb defused @"<< hex << (int)this << endl;
    }

    void executeStepFunction(const boost::system::error_code& error)
    {
        // Canceled timer
        if (error ==  boost::asio::error::operation_aborted)
        {
            std::cout << "Timer aborted: " << error.message() << " @" << std::hex << (int)this << std::endl;
            return;
        }
        if (m_active)
        {
            // Schedule next step
            cout << "tick .." <<endl;
            m_runTimer.expires_from_now(
                    boost::posix_time::millisec(1000));
            m_runTimer.async_wait(boost::bind(&TimeBomb::executeStepFunction, this, _1));
        }
    }
};

struct BomberMan
{
    asio::deadline_timer m_selfDestructTimer;
    TimeBomb* myBomb;

    BomberMan(boost::asio::io_service& ioService)
    : m_selfDestructTimer(ioService)
    {
        cout << "BomberMan ready " << endl;
        myBomb = new TimeBomb(ioService);
        m_selfDestructTimer.expires_from_now(boost::posix_time::millisec(10500));
        m_selfDestructTimer.async_wait(boost::bind(&BomberMan::defuseBomb, this, _1));
    }

    void defuseBomb(const boost::system::error_code& error)
    {
        cout << "Defusing TimeBomb" << endl;
        delete myBomb;
    }
};


int main()
{
    boost::asio::io_service m_ioService;
    BomberMan* b = new BomberMan(m_ioService);
    m_ioService.run();
    return 0;
}


./aTimeToKill
BomberMan ready 
Bomb placed @9c27198
tick ..
tick ..
tick ..
tick ..
tick ..
tick ..
tick ..
tick ..
tick ..
tick ..
Defusing TimeBomb
Bomb defused @9c27198
Timer aborted: Operation canceled @9c27198
//============================================================================
//名称:aTimeToKill.cpp
//作者:佩尔
//描述:使用计时器从计时器中删除对象
//============================================================================
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
使用名称空间boost;
结构定时炸弹
{
布尔穆活跃;
asio::截止时间计时器m\U运行计时器;
定时炸弹(boost::asio::io_服务和ioService)
:m_激活(真)
,m_运行计时器(iService)
{

cout这就是为什么您有
boost::shared\u ptr
boost::enable\u shared\u from\u This
。继承
TimeBomb
类from
boost::enable\u shared\u from\u This
如下:

struct TimeBomb : public boost::enable_shared_from_this< TimeBomb >
{
...
}

当然,
TimeBomb
类应该公开一个
cancel
方法,通过该方法可以取消异步操作,而不是通过删除或在本例中重置共享的ptr。

解决此问题的典型方法是使用
共享的ptr

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>

#include <iostream>

using namespace std;

struct TimeBomb : public boost::enable_shared_from_this<TimeBomb>
{
    bool m_active;
    boost::asio::deadline_timer m_runTimer;

    TimeBomb(boost::asio::io_service& ioService)
    : m_active(true)
    , m_runTimer(ioService)
    {
        cout << "Bomb placed @"<< hex << this << endl;
        m_runTimer.expires_from_now(boost::posix_time::millisec(1000));
    }

    void start()
    {
        m_runTimer.async_wait(boost::bind(&TimeBomb::executeStepFunction, shared_from_this(), _1));
    }

    void stop()
    {
        m_runTimer.cancel();
    }

    ~TimeBomb()
    {
        m_active = false;
        m_runTimer.cancel();
        cout << "Bomb defused @"<< hex << this << endl;
    }

    void executeStepFunction(const boost::system::error_code& error)
    {
        // Canceled timer
        if (error ==  boost::asio::error::operation_aborted)
        {
            std::cout << "Timer aborted: " << error.message() << " @" << std::hex << this << std::endl;
            return;
        }
        if (m_active)
        {
            // Schedule next step
            cout << "tick .." <<endl;
            m_runTimer.expires_from_now(
                    boost::posix_time::millisec(1000));
            m_runTimer.async_wait(boost::bind(&TimeBomb::executeStepFunction, shared_from_this(), _1));
        }
    }
};

struct BomberMan
{
    boost::asio::deadline_timer m_selfDestructTimer;
    boost::shared_ptr<TimeBomb> myBomb;

    BomberMan(boost::asio::io_service& ioService)
    : m_selfDestructTimer(ioService)
    {
        cout << "BomberMan ready " << endl;
        myBomb.reset( new TimeBomb(ioService) );
        myBomb->start();
        m_selfDestructTimer.expires_from_now(boost::posix_time::millisec(10500));
        m_selfDestructTimer.async_wait(boost::bind(&BomberMan::defuseBomb, this, _1));
    }

    void defuseBomb(const boost::system::error_code& error)
    {
        cout << "Defusing TimeBomb" << endl;
        myBomb->stop();
    }
};


int main()
{
    boost::asio::io_service m_ioService;
    BomberMan* b = new BomberMan(m_ioService);
    m_ioService.run();
    return 0;
}
#包括
#包括
#包括
#包括
#包括
使用名称空间std;
struct TimeBomb:public boost::从此\u启用\u共享\u
{
布尔穆活跃;
boost::asio::deadline\u timer m\u runTimer;
定时炸弹(boost::asio::io_服务和ioService)
:m_激活(真)
,m_运行计时器(iService)
{

Sam Miller的共享答案之所以有效,是因为使用共享的ptr会让定时炸弹在轰炸机人的一生中一直存在。这对你来说可能是好的,也可能不是

一个更完整的解决方案的建议是从工厂获取定时炸弹实例,然后在完成后将它们释放回工厂,而不是显式地更新和删除它们(将它们作为标准指针保留,而不是共享的\u ptr,因为即使您在控制生命周期,您也不拥有它们)。工厂可以保留它们,直到它们被取消,然后为您删除它们。保持Sam Miller的stop()函数不变

要实现这一点,请从接口沿

class ITimeBombObserver 
{
public:
    virtual void AllOperationsComplete(TimeBomb& TmBmb)=0;
};
将您的工厂作为ITimeBombomObserver传递给每个定时炸弹,并取消定时炸弹调用此功能。工厂可以在每次创建或发布定时炸弹时清理“使用过的”定时炸弹,或使用计划清理或其他方法,以最适合您的应用程序为准


使用此方法,BomberMan甚至不需要在defuseBomb()中显式释放定时炸弹。如果不需要,对stop()的调用可以自动释放(尽管在这种情况下,您仍然应该将指针设为null,因为此时指针实际上不可用)。这是否是一个好主意取决于您的实际问题,因此我将由您来决定。

对于一个非常简单的解决方案,这个怎么样?(我只包括了您需要更改的部分)

它之所以有效,是因为您只在计时器取消时访问堆栈变量。当然,您根本不需要在析构函数中回调处理程序,但我假设您的实际代码出于任何原因都需要这样做

~TimeBomb()
{
    m_active = false;
    executeStepFunction(boost::asio::error::interrupted);
    m_runTimer.cancel();
    cout << "Bomb defused @"<< hex << (int)this << endl;
}

void executeStepFunction(const boost::system::error_code& error)
{
    // Canceled timer
    if (error ==  boost::asio::error::operation_aborted)
    {
        return;
    }
    if (error ==  boost::asio::error::interrupted)
    {
        std::cout << "Timer aborted: " << error.message() << " @" << std::hex << (int)this << std::endl;
        return;
    }
...
~定时炸弹()
{
m_active=false;
executeStepFunction(boost::asio::error::interrupted);
m_runTimer.cancel();

我觉得你应该把定时炸弹的所有权交给Bomberman。一般来说(不看q的细节)您可以取消计时器或延长被调用方的生命周期。在C++中,最好通过调用调用代码至少部分所有权,或者通过调用函数来实现静态函数来完成。标准库提供了处理生命周期的智能指针。没有删除类之类的东西。这里的问题是关于删除的对象。感谢您展示正确的方法来执行此操作,尽管这可能意味着我必须重构大量代码。只需确保在删除定时炸弹的同一线程中处理回调。否则共享\u ptr可能会混乱。@用户不确定我是否遵循您的评论,线程有什么问题?@SamMiller您说得对。但我认为m_active什么都不做。
class ITimeBombObserver 
{
public:
    virtual void AllOperationsComplete(TimeBomb& TmBmb)=0;
};
~TimeBomb()
{
    m_active = false;
    executeStepFunction(boost::asio::error::interrupted);
    m_runTimer.cancel();
    cout << "Bomb defused @"<< hex << (int)this << endl;
}

void executeStepFunction(const boost::system::error_code& error)
{
    // Canceled timer
    if (error ==  boost::asio::error::operation_aborted)
    {
        return;
    }
    if (error ==  boost::asio::error::interrupted)
    {
        std::cout << "Timer aborted: " << error.message() << " @" << std::hex << (int)this << std::endl;
        return;
    }
...