1

ここで重大な問題が発生しました。1 つのシングルトン オブジェクト「VarList」にアクセスする 2 つの異なるタスク (プロセスなど) が PLC X20 フォーム B&R で実行されています。その目的は、ポインターを介したプロセス間通信を可能にすることです。

タスク A は、静的な getInstance() メソッドを使用して std::map を含むオブジェクトを作成しています。そのオブジェクトがグローバル PLC 変数に割り当てられている場合は、すべてのタスクからその特定のオブジェクトへのアクセスを許可するポインタがあります。そのタスクは、そのマップへの変数の挿入について、すべてのタスク サイクルもチェックしています。その場合、テスト目的で、挿入されたノードを再度削除しようとします。

タスク B は、getInstance メソッドを介してオブジェクトのポインターを取得し、bool* をマップに挿入します。

その後、タスク A が再度削除しようとすると、アクセス違反でクラッシュします。ノードとその値にアクセスできます。私はその価値観を変えることができます。しかし、別のタスクで作成されたノードを消去するとクラッシュするので、その理由を知りたいです!

PLC はシングルスレッドであるため、メモリへの同時アクセスはありません。

これはロックの問題ですか?それともこれはロジックの問題ですか?STLの問題?Null ポインタの問題? それとも、plc ベンダーの std ライブラリの特定の実装の問題でしょうか? アクセス違反に関連するヘルプは大歓迎です!

上記のオブジェクトのコード スニペットを次に示します。ヒントを得るために、問題に関係のないものはすべて削除しました。申し訳ありませんが、コンパイルできない可能性が最も高いです:

コンパイラ: 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    
    }
}

ありがとう!

4

2 に答える 2

2

個別のアドレス空間を持つプロセス (またはタスク) 間の任意の種類の共有メモリに STL マップを配置することは、変更なしでは確実に実行できません。

問題は、プロセス A がマップにデータを挿入すると、プロセス B からアクセスできない独自のアドレス空間から新しいメモリが割り当てられることです (マップは新しいエントリに内部的にメモリを割り当てます)。新しく挿入されたデータにアクセスしようとすると、エラー (アクセス違反など) が発生する可能性が非常に高くなります。

幸いなことに、STL コンテナーでは、アロケーターをオーバーライドして独自のものに置き換えることができます。独自のアロケータが、プロセス自身のアドレス空間ではなく共有メモリからメモリを割り当てることを保証する場合、物事はよりうまく機能するはずです。

于 2013-04-30T13:37:05.063 に答える
1

標準の実装では、ロックはありません/必要ありません。

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

もちろん、必要に応じて、実装にロックを含めることは許容されます。

于 2013-04-30T13:12:13.803 に答える