【问题标题】:Add and remove from a list in runtime在运行时从列表中添加和删除
【发布时间】:2013-07-20 18:45:10
【问题描述】:

我有一个模拟程序。在模拟的主类中,我正在“创建+添加”和“删除+销毁”代理。

问题是有时(我每运行程序 3-4 次)程序崩溃,因为我显然在主循环中调用了无效代理的函数。该程序大部分时间都运行良好。列表中通常有数千个代理。

  • 我不知道我的循环中怎么可能有无效代理。
  • 调试代码非常困难,因为我在“Agent::Step 函数”中收到内存异常(这为时已晚,因为我无法理解列表中的无效代理是如何被调用的)。

    当我查看 Agent::Step 函数(异常点)中的代理引用时,代理中的任何数据都没有意义,甚至初始化数据也没有意义。所以肯定是无效的。

    void World::step()
    {
        AddDemand();
    
        // run over all the agents and check whether they have remaining actions
        // Call their step function if they have, otherwise remove them from space and memory
        list<Agent*>::iterator it = agents_.begin();
        while (it != agents_.end())
        {
            if (!(*it)->AllIntentionsFinished())
            {
                (*it)->step();
                it++;
            }
            else
            {
                (*it)->removeYourselfFromSpace();  //removes its reference from the space
                delete (*it);
                agents_.erase(it++);
            }
        }
    }
    
    void World::AddDemand()
    {
        int demand = demandIdentifier_.getDemandLevel(stepCounter_);
        for (int i = 0; i < demand; i++)
        {
            Agent*  tmp  = new Agent(*this);
            agents_.push_back(tmp);
        }
    }
    
    Agent:
    
    bool Agent::AllIntentionsFinished()
    {
        return this->allIntentionsFinished_;  //bool flag will be true if all work is done
    }
    

1- 是否有可能是 VStudio 2012 优化循环(即如果可能在多线程中运行)会产生问题?

2- 对调试代码有什么建议吗?

【问题讨论】:

    标签: c++ list debugging stl


    【解决方案1】:

    如果您正在多线程运行代码,那么您需要添加代码来保护诸如添加项目和从列表中删除项目之类的事情。您可以相当轻松地创建一个为容器添加线程安全性的包装器——拥有一个互斥锁,您可以在任何时候对底层容器进行可能的修改操作时锁定该互斥锁。

    template <class Container>
    thread_safe {
        Container c;
        std::mutex m;
    public:
        void push_back(typename Container::value_type const &t) { 
             std::lock_guard l(m);
             c.push_back(t);
        }
        // ...
    };
    

    其他几点:

    1. 通过让列表直接保存Agents,而不是指向必须动态分配的代理的指针,您几乎可以肯定地清理您的代码。

    2. 您的Agent::RemoveYourselfFromSpace 看起来/听起来很像应该由代理的析构函数处理的东西。

    3. 几乎可以肯定,您可以通过使用一些标准算法来做更多的工作来清理代码。

    例如,在我看来,您的step 可以这样写:

    agents.remove_if([](Agent const &a) { return a.AllIntentionsFinished(); });
    
    std::for_each(agents.begin(), agents.end(),
                  [](Agent &a) { a.step(); });
    

    ...或者,您可能更喜欢继续使用显式循环,但使用类似:

    for (Agent & a : agents)
        a.step();
    

    【讨论】:

    • 感谢 cmets。 Agent::RemoveYourselfFromSpace 仅从名为 Space 的数组中删除代理引用(因此它不会出现在空间中)。它不触及其他任何东西。我有数百个类,使用代理指针可以更容易地避免类之间的链独立性。也感谢其他建议。
    • @Jerry Coffin 你的意思是“std::lock”还是“std::lock_guard<:mutex>”?我以为 std::lock 是a function
    【解决方案2】:

    问题是这样的:

    agents_.erase(it++);
    

    Add and remove from a list in runtime

    我在您显示的代码中没有看到任何线程安全组件,因此如果您正在运行多个线程并在它们之间共享数据,那么您绝对可能遇到线程问题。例如,您这样做:

    (*it)->removeYourselfFromSpace();  //removes its reference from the space
    delete (*it);
    agents_.erase(it++);
    

    这是未锁定列表的最差顺序。您应该:从列表中删除、销毁对象、删除对象,按此顺序。

    但如果您不是专门创建共享列表/代理的线程,那么线程可能不是您的问题。

    【讨论】:

    • 谢谢。它是否也适用于列表(因为我使用列表而不是向量)。我不使用线程,但我认为 vstudio 2012 编译器在认为没问题的情况下会尝试使用线程。
    • 嘿,是的 - 抱歉,那是错误的链接 - 它实际上是我试图链接的页面引用的位置,但链接到列表页面会更有意义:)
    • 更改顺序并不能解决问题。使用“它 = 代理擦除(它);”也不能解决问题。我想我应该再认真看看我的所有代码。顺便说一句,您的链接也指向同一个问题。
    • 使用电话接听电话的危险。 stackoverflow.com/questions/596162/… - 你的代码是多线程的(你是专门创建线程并为它们分配代理吗?)如果不是,那么顺序就无关紧要了。
    猜你喜欢
    • 1970-01-01
    • 2012-02-24
    • 1970-01-01
    • 1970-01-01
    • 2014-11-22
    • 1970-01-01
    • 1970-01-01
    • 2020-08-06
    • 2022-01-08
    相关资源
    最近更新 更多