C++ でシングルトン クラスを作成する必要がある場合は、静的変数、プライベート コンストラクター、およびクラスのオブジェクトを返すパブリック静的関数を使用します。ただし、マルチスレッド環境では、コードに問題があります。複数のスレッドが同時に同じ変数にアクセスするのを避けるために、Boost スレッドは同期に使用するのに最適なメカニズムですか? リソース全体でロック/ミューテックスを設定/設定解除することを意味します。ブーストをダウンロードしたり、ビルドしたりする必要がないC++標準ライブラリに組み込まれているものは他にありますか? C++ Ox については聞いたことがありますが、あまり知りません。
2 に答える
C++98/03 には、スレッドをサポートするものはまったくありません。C++98 または 03 コンパイラを使用している場合は、Boost や、pthreads や Win32 のスレッド化プリミティブなどの (多かれ少なかれ) OS 固有のものの使用にかなり行き詰まっています。
C++11 には、ミューテックス、ロック、スレッドローカル ストレージなどを備えた、かなり完全なスレッド サポート ライブラリがあります。
ただし、バックアップして、シングルトンが必要かどうか、または必要かどうかについてもう少し考えた方がよいかもしれないことを指摘しなければならないと感じています。うまく言えば、シングルトン パターンはかなり支持されなくなりました。
編集:これを読み直して、私が言いたかったことを1つスキップしました.少なくとも私がそれらを使用したとき、セカンダリスレッドが開始される前に、すべてのシングルトンが完全に初期化されました. これにより、初期化におけるスレッドの安全性に関する懸念は完全に無意味になります。セカンダリ スレッドを開始する前に初期化できないシングルトンが存在する可能性があるため、これに対処する必要があると思いますが、少なくともすぐに、かなり異常な例外として私を驚かせ、次の場合にのみ対処します。 /どうしても必要な場合。
私にとって、c++11 を使用してシングルトンを実装する最良の方法は次のとおりです。
class Singleton
{
public:
static Singleton & Instance()
{
// Since it's a static variable, if the class has already been created,
// It won't be created again.
// And it **is** thread-safe in C++11.
static Singleton myInstance;
// Return a reference to our instance.
return myInstance;
}
// delete copy and move constructors and assign operators
Singleton(Singleton const&) = delete; // Copy construct
Singleton(Singleton&&) = delete; // Move construct
Singleton& operator=(Singleton const&) = delete; // Copy assign
Singleton& operator=(Singleton &&) = delete; // Move assign
// Any other public methods
protected:
Singleton()
{
// Constructor code goes here.
}
~Singleton()
{
// Destructor code goes here.
}
// And any other protected methods.
}
これは C++11 の機能ですが、この方法でスレッド セーフなシングルトンを作成できます。新しい標準によれば、この問題を気にする必要はもうありません。オブジェクトの初期化は 1 つのスレッドによってのみ行われ、他のスレッドはそれが完了するまで待機します。または、std::call_once を使用できます。
シングルトンのリソースに排他的にアクセスしたい場合は、これらの関数でロックを使用する必要があります。
さまざまなタイプのロック:
atomic_flg_lck の使用:
class SLock
{
public:
void lock()
{
while (lck.test_and_set(std::memory_order_acquire));
}
void unlock()
{
lck.clear(std::memory_order_release);
}
SLock(){
//lck = ATOMIC_FLAG_INIT;
lck.clear();
}
private:
std::atomic_flag lck;// = ATOMIC_FLAG_INIT;
};
アトミックの使用:
class SLock
{
public:
void lock()
{
while (lck.exchange(true));
}
void unlock()
{
lck = true;
}
SLock(){
//lck = ATOMIC_FLAG_INIT;
lck = false;
}
private:
std::atomic<bool> lck;
};
ミューテックスの使用:
class SLock
{
public:
void lock()
{
lck.lock();
}
void unlock()
{
lck.unlock();
}
private:
std::mutex lck;
};
Windows専用:
class SLock
{
public:
void lock()
{
EnterCriticalSection(&g_crit_sec);
}
void unlock()
{
LeaveCriticalSection(&g_crit_sec);
}
SLock(){
InitializeCriticalSectionAndSpinCount(&g_crit_sec, 0x80000400);
}
private:
CRITICAL_SECTION g_crit_sec;
};
アトミックおよびアトミック_flg_lckは、スレッドをスピン カウントに保持します。Mutexはスレッドをスリープさせるだけです。待機時間が長すぎる場合は、スレッドをスリープさせたほうがよいでしょう。最後の「CRITICAL_SECTION」は、時間が消費されるまでスレッドをスピンカウントに保ち、その後スレッドはスリープ状態になります。
これらのクリティカル セクションの使用方法
unique_ptr<SLock> raiilock(new SLock());
class Smartlock{
public:
Smartlock(){ raiilock->lock(); }
~Smartlock(){ raiilock->unlock(); }
};
ライイイディオムを使用します。クリティカル セクションをロックするコンストラクタとロックを解除するデストラクタ。
例
class Singleton {
void syncronithedFunction(){
Smartlock lock;
//.....
}
}
変数ロックがスタックに保存されるため、この実装はスレッド セーフかつ例外セーフです。そのため、関数スコープが終了すると (関数の終了または例外)、デストラクタが呼び出されます。
これがお役に立てば幸いです。
ありがとう!!