以前、以下の記事で作成したSingletonパターンがスレッドセーフではなかったため、本記事ではスレッドセーフなSingletonパターンを実装していきます。
Singletonパターンのクラス構成
今回作成する、スレッドセーフなSingletonパターンのクラス構成は以下のとおりです。
Singletonパターンを実装する場合、インスタンスの作成をgetInstanceメソッドで行うということをしがちですが、この場合複数個のスレッドから同時に呼ばれたときに別のインスタンスが作成されるという問題がありました。
そこで今回は新たにcreateメソッドを実装して、ここでインスタンスを作成することでスレッドセーフなSingletonパターンを作成します。
また新しくインスタンスを破棄するdestroyメソッドも追加しました。
C++による実装
サンプルとして複数のスレッドからgetInstanceメソッドを呼ばれても一つしかインスタンスを作成しないということを証明するプログラムを作成します。
Singletonクラス
ヘッダファイル
#ifndef H_SINGLETON
#define H_SINGLETON
class Singleton{
private:
Singleton();
~Singleton();
public:
static void create();
static void destroy();
static Singleton* getInstance();
private:
static Singleton* m_singleton;
};
#endif // H_SINGLETON
ソースファイル
#include <iostream>
#include "Singleton.h"
Singleton* Singleton::m_singleton = nullptr;
Singleton::Singleton()
{
std::cout << "インスタンスを生成しました" << std::endl;
}
Singleton::~Singleton(){
std::cout << "インスタンスを削除しました" << std::endl;
}
void Singleton::create(){
if (!m_singleton){
m_singleton = new Singleton();
}
}
void Singleton::destroy(){
if (m_singleton){
delete m_singleton;
m_singleton = nullptr;
}
}
Singleton* Singleton::getInstance(){
if (!m_singleton){
return m_singleton;
}
return nullptr;
}
main関数
上記で作成したSingletonクラスを実際に使用してみます。
今回Windows環境で作成したため、スレッドはWin32 APIを使用して作成しています。
ソースファイル
#include <iostream>
#include <windows.h>
#include "Singleton.h"
#define LOOP 10
#define THREADNUM 10
struct stThreadArg{
unsigned long threadNum;
};
DWORD WINAPI thread(LPVOID *arg){
//struct stThreadArg* threadArg = (struct stThreadArg*)arg;
unsigned long* threadArg = (unsigned long*)arg;
std::cout << "Start thread" << *threadArg << std::endl;
for(int i=0; i<LOOP; ++i){
Singleton::getInstance();
}
std::cout << "End thread" << *threadArg << std::endl;
return 0;
}
int main(void){
std::cout << "Start" << std::endl;
// インスタンスを作成
Singleton::create();
/* スレッドの生成 */
HANDLE handle[THREADNUM];
unsigned long threadArg[THREADNUM];
for (int i=0; i<THREADNUM; ++i){
threadArg[i] = i;
handle[i] = CreateThread(0, 0, (LPTHREAD_START_ROUTINE) thread, &threadArg[i], 0, NULL);
}
WaitForMultipleObjects(THREADNUM, handle,TRUE, INFINITE);
// インスタンスを削除
Singleton::destroy();
std::cout << "End" << std::endl;
return 0;
}
実行結果
Start
インスタンスを生成しました
Start threadStart thread2
End thread2
Start thread9
End thread9
Start thread4
End thread4
Start thread3
End thread3
Start thread7
End thread7
Start thread8
End thread8
0
End thread0
Start thread6
End thread6
Start thread5
End thread5
Start thread1
End thread1
インスタンスを削除しました
End
実行結果からインスタンスの生成がcreateメソッドを呼んだときしか行っていないことが分かりますね。
また、destroyメソッドによってインスタンスの削除もしっかり行われています。
まとめ
Singletonパターンを作成するときは、スレッドセーフではない実装を行うようにしましょう。
もし仮にスレッドセーフではないSingletonパターンを作成したとして、不具合が起きていない場合、それはたまたま不具合が起きなかっただけにすぎません。
できる限り不具合が発生する要因をなくしていきましょう。