多分、こんなんでいいはず。
先日書いた記事 - Singletonクラスのアクセスを簡単にマルチスレッド対応させたい - krustf の雑記 で書きましたけど、最終的に今書いてるライブラリに追加したものです。
MSVC10, boost1.44.0で動作確認しました。
#include <utility> #include <boost/thread/mutex.hpp> // マルチスレッド // ------------------------------------------- template< class T > class multi_thread_access { public: multi_thread_access( T* ) : Lock_( SingletonMutex_ ) {}; multi_thread_access( multi_thread_access&& other ) : Lock_( std::move( other.Lock_ ) ) {}; multi_thread_access() : Lock_() {}; private: boost::mutex::scoped_lock Lock_; static boost::mutex SingletonMutex_; }; // class<T> の唯一の Mutex インスタンス template< class T > boost::mutex multi_thread_access<T>::SingletonMutex_; // シングルスレッド // ------------------------------------------- template< class T > class single_thread_access { public: single_thread_access( T* ) {}; single_thread_access( single_thread_access&& ) {}; single_thread_access() {}; }; // シングルトンポインタ // AccessPolicy default - multi_thread_access<T> // ------------------------------------------- template< class T , template< class U > class AccessType=multi_thread_access > class singleton_ptr : private AccessType<T> { public: typedef typename T value_type; typedef typename value_type * pointer; typedef typename value_type const* const_pointer; typedef typename value_type & reference; typedef typename value_type const& const_reference; singleton_ptr( pointer ptr ) : Instance_( ptr ) , AccessType<T>( ptr ) {}; singleton_ptr( singleton_ptr&& other ) : Instance_( other.Instance_ ) , AccessType<T>( std::move( other ) ) { other.Instance_ = NULL; }; singleton_ptr() : Instance_( NULL ) , AccessType<T>() {}; // 通常は -> でアクセスするのみ pointer operator->() { return get(); }; const_pointer operator->() const { return get(); }; // 参照を受け取る pointer get() { return Instance_; }; const_pointer get() const { return Instance_; }; // * でのアクセス reference operator*() { return *Instance_; }; const_reference operator*() const { return *Instance_; }; // インスタンスポインタを保持しているか operator bool() const { return Instance_ != NULL; }; private: // Singleton へのポインタ pointer Instance_; // copy が暗黙的に呼ばれないようにする singleton_ptr& operator=( singleton_ptr& ); singleton_ptr( singleton_ptr& ); // move はコンストラクタのみを許可 singleton_ptr& operator=( singleton_ptr&& ); };
デフォルトはマルチスレッド用のアクセスポリシを持つようになっていて、boost::mutexでアクセス制御します。
コンストラクタにはわざとexplicitをつけていません。
後は Singleton クラスのインスタンス取得関数の戻り値に singleton_ptr
#include "singleton_ptr.h" #include <vector> // 適当なSingletonクラス class hoge { public: typedef std::vector<int> value_type; value_type& get() { return DataList_; }; // singleton_ptr<T>で返すインスタンス取得関数を作る typedef krustf::singleton_ptr<hoge> instance_type; static instance_type get_instance(); private: value_type DataList_; hoge() : DataList_() {}; hoge( const hoge& ); hoge( hoge&& ); hoge& operator=(const hoge&); hoge&& operator=(const hoge&&); }; // Singleton インスタンスのポインタを返却 hoge::instance_type hoge::get_instance() { static hoge _Instance; return &_Instance; } #include <boost/thread.hpp> #include <iostream> #include <algorithm> // スレッド用 // データを挿入 void func1() { for( int i = 0 ; i < 30 ; ++i ) { auto x = hoge::get_instance(); x->get().push_back( i ); boost::thread::sleep( boost::get_system_time() + boost::posix_time::milliseconds(1) ); } } // データを変更 void func2() { for( int i = 0 ; i < 40 ; ++i ) { auto x = hoge::get_instance(); std::for_each( x->get().begin() , x->get().end() , []( int& j ) { ++j; } ); boost::thread::sleep( boost::get_system_time() + boost::posix_time::milliseconds(1) ); } } int main() { boost::thread_group tgrp; tgrp.create_thread( &func1 ); tgrp.create_thread( &func2 ); tgrp.join_all(); auto x = hoge::get_instance(); std::for_each( x->get().begin() , x->get().end() , [] ( int i ) { std::cout << i << " "; } ); }
autoを使うことでかなり楽になります。
束縛している間に他のスレッドでget_instance()を呼び出すと、singleton_ptr
結果はこういう感じになるはずです。こればかりは実行環境によって結果が異なると思うのですが、値が壊れてなければ問題ないはずです。
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
問題点としては、自身のスレッドで既にsingleton_ptr
対処法としては他の関数を呼び出す際には既に所有しているsingleton_ptr
// 同一スレッドから呼び出す関数 void func( hoge::instance_type& x ) { /// x を使って何か処理 } void hoge() { auto x = hoge::get_instance(); // x を使って何か処理 func( x ); }
これだけを気をつければ、singleton_ptr
シングルスレッド用のMutexロックをしないバージョンも用意しました。マルチスレッドで動かさない場合はこちらにしておいた方がいいでしょう。むしろMutexのロックの所為で処理時間を無駄に使います。