2016年3月24日木曜日

シングルトンを作る

おじさん、シングルトンについて勉強しました。
プログラムでインタフェースの勉強をすると一番最初に出てくるあれです。

C/C++でシングルトンを作るのって本当に難しいです。
これでもかというほど難しいのです。
おじさん、40歳を過ぎても、いまだにシングルトンを正しくかけず。


そこで本日はC++で何パターンかシングルトンを自分で作ってみました。

パターン3は遅いからよくないのはわかるんですが、じゃあどう書けばよいのかが全く分かりません。

ネットではパターン5でないといけないっぽいことが書かれているんですが、意味わからないし、読みづらいし、めんどくさいし・・・。

Googleの人の書いているAndroidのソースコードを見るとパターン6っぽいことをやっているので、僕的にはパターン6で行ける感じがするんですが、パターン6はそもそも本当に正しいのかなぁ?

昔は簡単でよかったんですけどね・・・・。何も悪いことしていないのになぜか僕の給料とシングルトンは勝手にどんどん状況が悪くなっています。
いまだにおじさん、シングルトンわからずです。もう引退かな。



------------------------------------
#include <stdio.h>
#include <thread>
#include <mutex>
#include <atomic>



class A{
public:
A();
virtual ~A();
static A* getInstance_1();
static A* getInstance_2();
static A* getInstance_3();
static A* getInstance_4();
static A* getInstance_5();
static A* getInstance_6();
};

A::A(){
printf("A::A() start\n");
std::this_thread::sleep_for(std::chrono::seconds(5));
printf("A::A() end\n");
}


A::~A(){
printf("A::~A()\n");
}


//
// Ptn1 OK  Static Initialization
//

A* A::getInstance_1()
{
static A s_a;
return &s_a;
}

//
// Ptn2 NG  Dynamic Initialization
//

A* A::getInstance_2()
{
static A* s_a = NULL;
if (s_a == NULL){
s_a = new A();
}
return s_a;
}


//
// Ptn3 OK  Mutex Locked Dynamic Initialization
//


A* A::getInstance_3()
{
static A* s_a = NULL;
static std::mutex mutex;

mutex.lock();
if (s_a == NULL){
s_a = new A();
}
mutex.unlock();
return s_a;
}



//
// Ptn4 NG  Double Checked Locking
//

A* A::getInstance_4()
{
static A* s_a = NULL;
static std::mutex mutex;
if (s_a == NULL){
mutex.lock();
if (s_a == NULL){
s_a = new A();
}
mutex.unlock();
}
return s_a;
}

//
// Ptn5 OK  Atomic Double Checked Locking
//

A* A::getInstance_5() {
static std::atomic<A*> s_a;
static std::mutex mutex;

A* tmp = s_a.load(std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_acquire);
if (tmp == nullptr) {
std::lock_guard<std::mutex> lock(mutex);
tmp = s_a.load(std::memory_order_relaxed);
if (tmp == nullptr) {
tmp = new A();
std::atomic_thread_fence(std::memory_order_release);
s_a.store(tmp, std::memory_order_relaxed);
}
}
return tmp;
}


//
// Ptn6 OK Call Once
//


static A* s_a = NULL;
std::once_flag once_a;
void init_a(){
s_a = new A();
}

A* A::getInstance_6()
{
std::call_once(once_a, init_a);
return s_a;
}



//
// Test Code
//


void thread_main(){

std::this_thread::sleep_for(std::chrono::seconds(1));
printf("thread_main get start\n");
A* a = A::getInstance_6();
printf("thread_main get end\n");
}

int main()
{
A* a;

std::thread th(thread_main);
a = A::getInstance_6();

th.join();
delete a;

}

------------------------------------





0 件のコメント:

コメントを投稿