【问题标题】:Does a std::map lock its nodes to prevent other processes from deleting them?std::map 是否锁定其节点以防止其他进程删除它们?
【发布时间】:2013-04-24 10:05:01
【问题描述】:

我在这里遇到了一个严重的问题。 我有两个不同的任务(如一个进程)在我的 PLC X20 表单 B&R 上运行,访问一个单例对象“VarList”。其目的是允许通过指针进行进程间通信。

任务 A 正在使用静态 getInstance() 方法创建包含 std::map 的对象。如果将该对象分配给全局 PLC 变量以允许从每个任务访问该特定对象,则会出现指针。该任务还检查每个任务周期是否将变量插入到该映射中。在这种情况下,它会尝试再次删除该插入的节点,仅出于测试目的。

任务 B 通过 getInstance 方法获取对象的指针,并在映射中插入一个 bool*。

之后任务 A 尝试再次删除它,但它因访问冲突而崩溃。我可以访问节点及其值。我可以改变那个值。但是擦除在不同任务中创建的节点会导致崩溃,我想知道为什么!

PLC 是单线程的,因此不应有对内存的并发访问。

这是一个锁定问题吗?或者这是一个逻辑问题? STL 问题?空指针问题?或者可能又是plc供应商的std库的具体实现问题?任何与访问冲突相关的帮助表示赞赏!

这里是提到的对象的代码 sn-ps,我删除了与问题无关的所有内容,只是为了获得提示。抱歉,它很可能无法编译:

编译器:gcc 4.1.2

任务 A:

#include <VarListe.hpp>
VarListe::Ptr VLInstanz;

void _INIT VLErzeugerInit(void)
{
    VLInstanz = VarListe::getInstance("VLErzeuger");
}

void _CYCLIC VLErzeugerCyclic(void)
{
    VLInstanz->checkNewVars(); // Access Violation here
}

任务 B:

#include <VarListe.hpp>
VarListe::Ptr vals;

bool setPtr = true;


void _INIT VarListeTestInit(void)
{
}

// btn_VarTest is a Boolean plc Variable for a button on the Visu
void _CYCLIC VarListeTestCyclic(void)
{
    try
    {
        if(btn_VarTest &&setPtr) 
        {
            vals = VarListe::getInstance("VarListe1");
            vals->setVar("btn_VarTest",&btn_VarTest);   
            //vals->checkNewVars(); // Works perfect if used here. Thats not the point
            setPtr = false;
        }
    catch (...)
    {
    }
}

VarListe.hpp

#include <map>
#include <deque>
#include <string>
#include <boost/shared_ptr.hpp>

using namespace std;

class VarListe
{
    public: 

    typedef bool*           BoolPtr;

    typedef boost::shared_ptr<VarListe> Ptr;

    static Ptr getInstance(string owner);
    static Ptr _alwaysUseGetInstance;
    static char owners[200];

    void checkNewVars();

    private:
    typedef map<string, BoolPtr >           BoolPtrMap;

    typedef deque<BoolPtrMap::iterator>     BoolVarQueue;
    BoolVarQueue                            _boolVarQueue;

    BoolPtrMap      _boolListe;

    public:
    void setVar(string key, bool* value);
};

VarListe.cpp

#include <VarListe.hpp>

VarListe::Ptr VarListe::_alwaysUseGetInstance; // Singleton static Variable; used olny by the object creator
char    VarListe::owners[200]; // just to test which task creats the object

#include <../../Temp/Includes/globalvar.h> // For the global PLC variable 'GlobalVarListe' 

// This static Method runs perfectly, no need to check here
VarListe::Ptr VarListe::getInstance(string owner)
{

    if(GlobalVarListe == 1337)
    {
        strcpy(VarListe::owners, "");
        owner += "(Builder)";
        if (!VarListe::_alwaysUseGetInstance)
            VarListe::_alwaysUseGetInstance = VarListe::Ptr(new VarListe);
        GlobalVarListe = (UDINT) &VarListe::_alwaysUseGetInstance;
        VarListe::_alwaysUseGetInstance->setVar("VarListOwners",VarListe::owners);
    }
    VarListe::Ptr tempVL = *( (VarListe::Ptr*) GlobalVarListe);
    VarListe::CharPtrPair locOwners;
    if(tempVL->assertVar("VarListOwners",locOwners))
    {
        string temp = string(locOwners.first);
        temp = temp + owner + " + ";
        strcpy(locOwners.first, temp.c_str());
    }

    return tempVL;
}

// This Method is used in Task B
void VarListe::setVar(string key, bool* value)
{
    pair<BoolPtrMap::iterator, bool > eingetragen;
    eingetragen = _boolListe.insert(pair<string, BoolPtr>(key, value ));

    if(eingetragen.second == false)
    {
    } 
    else
        _boolVarQueue.push_back(eingetragen.first);
}

// This method is in Task A
void VarListe::checkNewVars()
{
    if(!_boolVarQueue.empty())
    {
        string key = _boolVarQueue.front()->first;  //OK, 
        BoolPtr bp = _boolVarQueue.front()->second; //OK
        _boolVarQueue.front()->second = 0;          //OK
        _boolListe[key] = bp;                       //OK
        BoolPtrMap::iterator fund = _boolListe.find(key); // OK
        if (fund != this->_boolListe.end())         // OK
        {       
            _boolListe.erase(key);                      //Access Violation: Code 9101 only if used by Task A 
            _boolListe.erase(_boolVarQueue.front());    //Access Violation: Code 9101 
            _boolListe.erase(fund);                     //Access Violation: Code 9101
        }
        _boolVarQueue.pop_front();                  //OK
        _boolListe[key] = bp;                       //OK    
    }
}

谢谢!

【问题讨论】:

  • 为什么使用指向bool的指针?直接存储bool简单得多。使用原始类型时,几乎没有使用指向它们的指针。
  • 很确定您可以将其缩减为一个较小的示例:请参阅sscce.org
  • @Joachim 我知道你的意思,但我的意图是与多个进程共享一个特定的内存位置。这就是为什么它是一个指针。
  • 这些独立的任务有独立的地址空间吗?
  • 这些任务是真正的多任务任务,即它们可以随时被抢占吗?那么是的,你需要某种保护。如果任务是顺序的或多任务是合作的,那么可能就不需要了。

标签: c++ locking stdmap plc


【解决方案1】:

将 STL 映射放置在具有单独地址空间的进程(或任务)之间的任何类型的共享内存中,如果不进行更改,就无法可靠地完成。

问题在于,如果进程 A 将数据插入映射,它将从自己的地址空间分配任何新内存(并且映射在内部为新条目分配内存),而进程 B 无法访问这些内存。所以,如果进程 B 然后尝试访问新插入的数据,它很可能会出错(例如访问冲突)。

幸运的是,STL 容器允许覆盖分配器以将其替换为您自己的。如果您自己的分配器然后确保它从共享内存而不是进程自己的地址空间分配内存,那么事情应该会更好。

【讨论】:

  • 有趣的是,到目前为止我从未使用过分配器,也许这可以解决我的问题。我现在会深入研究它。谢谢!
  • 如果有机会同时访问地图,您还必须按照其他答案添加锁定。如果您可以保证地图上的每个操作都是“原子”完成的(即,一个任务在任何其他任务对地图执行任何操作之前完全完成其操作),则不需要这样做。
  • 正如我所说,任务是完全顺序的,出于开发目的,我可以确保这些操作是“原子地”完成的,所以到目前为止这不是问题。稍后当所有内容放在一起时,访问地图时可能会暂停任务,但我知道这种问题。无论如何,谢谢!
【解决方案2】:

标准实现没有/不需要任何锁定:

http://www.sgi.com/tech/stl/thread_safety.html

当然允许实现包含锁,如果它希望这样做的话。

【讨论】:

  • 好吧,我在那个文档中读到,分配器中似乎存在某种锁定。这可能会导致我的问题,因为我只能删除另一个线程创建的节点。但我不确定......我在访问其他线程的内存时没有遇到任何其他问题
  • 对,访问冲突可能不是由于锁定。您更有可能尝试使用在不同任务中分配的内存(如上所述),而内存根本不属于当前正在运行的进程。
  • 我刚刚阅读了有关 boost 进程间分配器的信息,它可能会解决我的问题。我明天再问一个问题。
猜你喜欢
  • 1970-01-01
  • 2012-09-28
  • 2020-10-31
  • 1970-01-01
  • 2011-10-17
  • 2021-10-30
  • 1970-01-01
  • 2011-02-22
  • 1970-01-01
相关资源
最近更新 更多