2016-05-25 7 views
-1

テンプレートを定義するヘッダーファイルは次のとおりです。 いくつかの回答に基づいて、テンプレートのシングルスレッドを安全にするための修正を行いました。しかし、プログラムはまだクラッシュするので、ここでは、テンプレートがスレッドセーフであるかどうかに焦点を当てるべきではないと思います。 C1がCBaseとSingletonの両方から継承したときに起こったことです。どうやら、C1が親クラスCBaseのメンバー変数の関数を呼び出そうとすると、プログラムがクラッシュします。それは私には、複数の継承と単一のテンプレートを一緒に使用すると、何かがメモリに混乱しているようです。なぜシングルトンテンプレートがプログラムをクラッシュさせるのですか?

#ifndef _T_SINGLETON_HH_ 
#define _T_SINGLETON_HH_ 

template <class T> 
class Singleton 
{ 
public: 
    static T* getInstance() 
    { 
     static T instance_; 
     return &instance_; 
    } 

    template <class A> 
    static T* getInstance(A a) 
    { 
     static T instance_(a); 
     return &instance_; 
    } 

protected: 
    Singleton() {} 
    virtual ~Singleton() {} 
private: 
    Singleton(Singleton const&); 
    Singleton& operator=(Singleton const&); 
}; 
#endif // _T_SINGLETON_HH_ // 

そして、ここでは、このテンプレートを使用した方法です。

#include <iostream> 

#include <unistd.h> 
#include <pthread.h> 

#include "singleton.hh" 

using namespace std; 

class X; 
class X1; 
class ABase; 
class BBase; 
class BB; 
class CBase; 

class X { 
public: 
    virtual int getX() = 0; 
}; 

class X1 : public X { 
public: 
    int getX() { return 1; } 
}; 

class Timer 
{ 
private: 
    static Timer* t_; 
    BBase* b_; 

    Timer(); 
    static void* go_helper(void* context); 
    void go(); 

public: 
    virtual ~Timer() {} 

    static Timer* getInstance(); 
    void subscribe(BBase* b); 
    void unsubscribe(BBase* b) { b_ = NULL; } 
}; 

class CBase { 
public: 
    CBase(BB& bb) : bb_(bb) {} 
    virtual void run() = 0; 
protected: 
    BB& bb_; 
}; 

class C1 : public CBase, 
      public Singleton<C1> 
{ 
public: 
    C1(BB& bb) : CBase(bb) {} 
    void run(); 
}; 

class BBase { 
public: 
    BBase(ABase& a) : a_(a) { Timer::getInstance()->subscribe(this); } 
    virtual ~BBase() { Timer::getInstance()->unsubscribe(this); } 
    virtual void run() = 0; 
protected: 
    ABase& a_; 
}; 

class BB : public BBase { 
public: 
    BB(ABase& a) : BBase(a) { 
     c = C1::getInstance(*this); 
//  c = new C1(*this); IF WE USE THIS INSTEAD, THEN IT WILL WORK 
    } 
    int getX(); 
    void run(); 
private: 
    CBase* c; 
}; 

class ABase { 
public: 
    ABase() { 
     x = new X1; 
     b = new BB(*this); 
    } 
    void run(); 
    int getX() { return x->getX(); } 
private: 
    X* x; 
    BBase* b; 
}; 

Timer* Timer::t_ = NULL; 

Timer::Timer() : b_(NULL) 
{ 
    pthread_t th; 
    pthread_create(&th, NULL, &Timer::go_helper, this); 
} 

Timer* 
Timer::getInstance() { 
    if (t_ == NULL) 
     t_ = new Timer; 
    return t_; 
} 

void 
Timer::subscribe(BBase* b) { b_ = b; } 

void* 
Timer::go_helper(void* context) { 
    Timer *t = reinterpret_cast<Timer*>(context); 
    t->go(); 
    return NULL; 
} 

void 
Timer::go() 
{ 
    while(1) { 
     sleep(1); 
     if (b_) b_->run(); 
    } 
} 

void ABase::run() { 
    cout << __PRETTY_FUNCTION__ << getX() << endl; 
    cout << __PRETTY_FUNCTION__ << x->getX() << endl; 
    b->run(); 
    while(1) 
     sleep(1); 
} 

int BB::getX() { 
    return a_.getX(); 
} 
void BB::run() { 
    cout << __PRETTY_FUNCTION__ << endl; 
    c->run(); 
} 

void C1::run() { 
    cout << __PRETTY_FUNCTION__ << bb_.getX() << endl; 
} 

int main() 
{ 
    ABase* a = new ABase; 
    a->run(); 
} 

あなたはまだそれから、ここで、それはスレッドセーフな問題であると考えられる場合は、テンプレートを使用して簡素化1つのスレッドバージョンです:

#include <iostream> 

#include <unistd.h> 
#include <pthread.h> 

#include "singleton.hh" 

using namespace std; 

class X; 
class X1; 
class ABase; 
class BBase; 
class BB; 
class CBase; 

class X { 
public: 
    virtual int getX() = 0; 
}; 

class X1 : public X { 
public: 
    int getX() { return 1; } 
}; 

class CBase { 
public: 
    CBase(BB& bb) : bb_(bb) {} 
    virtual void run() = 0; 
protected: 
    BB& bb_; 
}; 

class C1 : public CBase, 
      public Singleton<C1> 
{ 
public: 
    C1(BB& bb) : CBase(bb) {} 
    void run(); 
}; 

class BBase { 
public: 
    BBase(ABase& a) : a_(a) { } 
    virtual ~BBase() { } 
    virtual void run() = 0; 
protected: 
    ABase& a_; 
}; 

class BB : public BBase { 
public: 
    BB(ABase& a) : BBase(a) { 
     c = C1::getInstance(*this); 
//  c = new C1(*this); 
    } 
    int getX(); 
    void run(); 
private: 
    CBase* c; 
}; 

class ABase { 
public: 
    ABase() { 
     x = new X1; 
     b = new BB(*this); 
    } 
    void run(); 
    int getX() { return x->getX(); } 
private: 
    X* x; 
    BBase* b; 
}; 

void ABase::run() { 
    cout << __PRETTY_FUNCTION__ << getX() << endl; 
    cout << __PRETTY_FUNCTION__ << x->getX() << endl; 
    b->run(); 
    while(1) 
     sleep(1); 
} 

int BB::getX() { 
    return a_.getX(); 
} 
void BB::run() { 
    cout << __PRETTY_FUNCTION__ << endl; 
    c->run(); 
} 

void C1::run() { 
    cout << __PRETTY_FUNCTION__ << bb_.getX() << endl; 
} 

私はこのプログラムを実行すると、それがクラッシュ:

void ABase::run()1 
void ABase::run()1 
virtual void BB::run() 
zsh: segmentation fault (core dumped) ./a.out 
いくつかの未知の理由

#0 0x00000000004012ba in ABase::getX (this=0x1) at tst_sigill.cc:89 
89  int getX() { return x->getX(); } 
[Current thread is 1 (Thread 0x7faa0f050740 (LWP 5351))] 
(gdb) bt 
#0 0x00000000004012ba in ABase::getX (this=0x1) at tst_sigill.cc:89 
#1 0x0000000000400e0c in BB::getX (this=0x7ffed6e821f0) at tst_sigill.cc:138 
#2 0x0000000000400e71 in C1::run (this=0xaeedd0) at tst_sigill.cc:146 
#3 0x0000000000400e51 in BB::run (this=0xaeec60) at tst_sigill.cc:142 
#4 0x0000000000400de3 in ABase::run (this=0xaeec20) at tst_sigill.cc:132 
#5 0x0000000000400ed1 in main() at tst_sigill.cc:152 

、BB ::のgetXとABASE ::のgetXに渡され、このポインタ台無しにされた:

はここでGDBによって与えられたスタック情報です。私はコードの何が間違っているのか分かりません。

+1

スタックトレースの 'this = 0x1'はかなり良い出発点です。 'x'は無効です。 – user4581301

答えて

2

大きな問題は、getInstance()の実装がスレッドセーフではないように見えることです。

私はすべてconstruct()/destruct()ものについても忘れて、単にABIに付属のCRT0コードに残すことをお勧めします。


あなたはむしろ、(少なくとも現在の標準用)スレッドセーフであることが保証されてScott Meyer's Singletonを次の実装を使用する必要があります。

template <class T> 
class Singleton { 
public: 
    static T& getInstance() { 
     static T instance_; 
     return instance_; 
    } 

protected:  
    Singleton() {} 
    virtual ~Singleton() {} 

    Singleton(Singleton const&) = delete; 
    Singleton& operator=(Singleton const&) = delete; 
}; 

Singletonのプロパティを変更する必要がある場合後でそれは非同期にアクセスすることができ、スレッドセーフな操作を提供することに留意してください。いくつかの有用な情報を狙い撃ちするためにスタックトレースを使用して

template <class T> 
class Singleton { 
    // ... 
protected:  
    mutable std::mutex internalDataGuard; 
}; 

class C1 : public CBase, public Singleton<C1> { 
public: 
    C1(BB& bb) : CBase(bb) {} 
    void run(); 

    void setSomeData(T data) { 
     std::unique_lock(Singleton<C1>::internalDataGuard); 
     // change internal data member ... 
    } 

    const T& getSomeData() const { 
     std::unique_lock(Singleton<C1>::internalDataGuard); 
     // return internal data member ... 
    } 
}; 
+0

それはしばらく後にOPを吹き飛ばすだろうが、OPのコードはそれほど遠くには達しないだろう。 – user4581301

+1

@ user4581301それは根本的な問題だと思いますが、OPは設計を大幅に変更しなければなりません。 –

+0

あなたの答えをありがとう、しかしこれは私の問題を解決しません。毎回クラッシュする。 – user3776886

0

簡単なメモを:

あなたのようなあなたのテンプレートシングルトンベースでそれをサポートすることができました。それがクラッシュしたプログラムがクラッシュ、時にはトレースがあなたに物事がうまく行くことを始めた場所にいくつかのヒントを与えることができます。

#0 0x00000000004012ba in ABase::getX (this=0x1) at tst_sigill.cc:89 

がここに死にました。この0x1は、thisがナンセンスを指していると言います。 0x1はほぼ確実に無効なメモリ位置です。私は1が有効な記憶である現代的なプラットフォームを考えることはできません。それがないことを意味するものではありませんが、それは非常に起こりそうもなく、したがって容疑者です。

#1 0x0000000000400e0c in BB::getX (this=0x7ffed6e821f0) at tst_sigill.cc:138 

ここでthisは疑わしいです。次の数行をスキップすると、thisポインタのすべてが0xaeeXXXとその周辺にあることがわかります。0x7ffed6e821f0はかなり遠くまでありますので、それは私たちが正気アドレスとしていた最後のオブジェクトだったので、私たちはBB

#2 0x0000000000400e71 in C1::run (this=0xaeedd0) at tst_sigill.cc:146 

C1内部具体的にそれらへの参照を見ることで開始する必要があります。

スタックの残りの部分が重要かもしれませんが、そうでないかもしれません。たぶんもっと手がかりを得るためにそれに戻ってくるかもしれませんが、現時点では、現在のリードを倒そうとします。

C1上で見ると、私は次を参照してください。

C1(BB& bb) : CBase(bb) {} 

CBase(BB& bb) : bb_(bb) {} 

店舗bb_

BBそうにBB参照を取り、CBaseのコンストラクタに渡しますBBはどこから来たのですか? C1は、シングルトンによってインスタンス化されます。私はconstructへの呼び出しを見ないので、これは参照を取らないgetInstance(A a)を残します。つまり、C1(BB& bb)が一時的に呼び出されます。テンポラリはであるCBase::bb_に最終的に格納されます。bb_は、getInstanceから戻った後も存在しなくなる変数への参照です。このポイントの後にbb_を使用しようとすると、未定義の動作が呼び出されます。

このコードは、あまりにも複雑であり、πάνταῥεhisが彼の答えで指摘しているように、重要な再設計なしでは動作しないため、修正を提案するつもりはありません。

+0

getInstance(A a)をgetInstance(A&a)に変更すると、私の問題は解決しました。私はBB&がテンプレートとしてAと見なされると思った。 – user3776886

+0

すぐに問題は解決しますが、あなたはまだ他の不満のために大きく開いています。πάνταῥεῖは警告し、これを解決した解決策を提供します。 – user4581301

関連する問題