C++ 自作マルチスレッドライブラリ
昔会社で利用していたC++のマルチスレッドライブラリを自作してみました。以下で構成されています。
- Javaライクなスレッドライブラリ
- CommandDispatcher(EventDispatcher) パターン (マルチスレッドデザインパターン)
実装
https://github.com/tsuyuzaki/cpp/tree/master/MultiThreadCode
(本来はヘッダをincludeディレクトリに分けるなどすべきですがべたにコードを並べてしまっています。)
Javaライクなスレッドライブラリ
Javaのスレッドライブラリの機能を最小限にして模倣したなんちゃってスレッドライブラリ説明。
CThreadクラス
CThreadクラスはインスタンスごとにスレッドを起動します。
起動したスレッドでコンストラクタの引数で渡されたIRunnableのrun()メソッドをcallします。
Publicメソッド
/**
* CThreadのインスタンスを生成します。
* 引数で渡したrunnableのrun()メソッドを別スレッドでcallします。
* インスタンスの生成に失敗すると 0 が返ります。
*/
static CThread *createInstance(
IRunnable *runnable);
/**
* 新しいスレッドを起動します。
* 1インスタンスで複数回呼び出すとassertします。
*/
bool start();
/**
* 起動したスレッドの完了を待ち合わせます。
* スレッドを起動したのち、呼び出しが可能です。
* 1インスタンスで複数回呼び出すとassertします。
*/
void join();
IRunnableクラス
CThread::createInstance()に渡すインスタンスが継承するインターフェースです。
IRunnableを継承したクラスのrun()メソッドに別スレッドで処理したい実装を定義します。
Publicメソッド
/**
* CThreadクラスのstartメソッドで起動したスレッドで実行されるメソッドです。
*/
virtual void run() = 0;
CSynchronizerクラス
スレッドの排他を行うためのクラスです。
Publicメソッド
/**
* CSynchronizerインスタンスを生成します。
* インスタンスの生成に失敗すると 0 が返ります。
*/
static CSynchronizer *createInstance();
/**
* 相互排他lockを行います。
*/
void lock();
/**
* 相互排他lockをunlockします。
*/
void unlock();
/**
* 待ち合わせ状態に遷移します。
*/
void wait();
/**
* 待ち合わせ状態を解除するシグナルをbroadcastします。
*/
void notifyAll();
CSynchronizedクラス
CSynchronizerのラッパークラスです。
CSynchronizedのコンストラクタにはCSynchronizerインスタンスのポインタを渡します。
コンストラクタでCSynchronizerのlock()を行い、デストラクタでunlock()を行います。
関数先頭行で以下を記述すると関数スコープに入るとlock()を、関数スコープを抜けるとunlock()をしてくれます。
CSynchronized sync(sync); // syncはCSynchronizerインスタンスのポインタ
Publicメソッド
/**
* CSynchronizerのwait()を呼び出します。
*/
void wait();
/**
* CSynchronizerのnotifyAll()を呼び出します。
*/
void notifyAll();
CommandDispatcher(EventDispatcher) パターン
CCommandDispatcherクラス
ICommandをキューイングし、ワーカースレッドでICommandのexecute()を逐次実行するクラスです。
ワーカースレッド数は1です。
キューイングしたICommandはCCommandDispatcherがdeleteします。クライアント側でdeleteしないでください。
Publicメソッド
/**
* CCommandDispatcherのインスタンスを生成します。
* インスタンスの生成に失敗すると 0 が返ります。
*/
static CCommandDispatcher *createInstance();
/**
* ICommandをキューイングします。
* キューイングされたICommandは逐次ワーカースレッドでexecute()呼び出しがされます。
* キューイングしたICommandは、execute()呼び出しが終わるとCCommandDispatcherによりdeleteされます。
*/
void pushBack(
ICommand *command);
/**
* キューイングされているICommandのexecute()呼び出しが全て完了するのを待ち合わせます。
*/
void waitForAllCommandsExecuted();
ICommandクラス
CCommandDispatcherにキューイングされるインスタンスが継承するインターフェースです。
ICommandを継承したクラスのexecute()にCCommandDispatcherのワーカースレッドで処理したい実装を定義します。
Publicメソッド
/**
* CCommandDispatcherのワーカースレッドで呼び出されるメソッド。
*/
virtual void execute() = 0;
利用例
/**
* コンストラクタの引数に渡したlogを1秒おきにsec秒間表示する。
*/
class CCommand:
public ICommand
{
public:
CCommand(
const char *log,
unsigned int sec)
...
};
int main()
{
CCommandDispatcher *dispatcher = CCommandDispatcher::createInstance();
assert(dispatcher != 0);
dispatcher->pushBack(new CCommand("1st", 5));
dispatcher->pushBack(new CCommand("2nd", 3));
dispatcher->pushBack(new CCommand("3rd", 7));
::std::cout << "Before waitForAllCommandsExecuted() call." << ::std::endl;
dispatcher->waitForAllCommandsExecuted();
::std::cout << "After waitForAllCommandsExecuted() call." << ::std::endl;
dispatcher->pushBack(new CCommand("4th", 2));
dispatcher->pushBack(new CCommand("5th", 5));
dispatcher->pushBack(new CCommand("6th", 3));
for (int i = 0; i < 10; i++) {
::std::cout << "Main thread" << ::std::endl;
sleep(1);
}
delete dispatcher;
return 0;
}
実行結果
$ rm a.out; g++ -lpthread *.h *.cpp; rm *.gch
$ ./a.out
Before waitForAllCommandsExecuted() call.
---- ENTER CCommand::execute() ----
1st
1st
1st
1st
1st
---- EXIT CCommand::execute() ----
---- ENTER CCommand::execute() ----
2nd
2nd
2nd
---- EXIT CCommand::execute() ----
---- ENTER CCommand::execute() ----
3rd
3rd
3rd
3rd
3rd
3rd
3rd
---- EXIT CCommand::execute() ----
After waitForAllCommandsExecuted() call.
Main thread
---- ENTER CCommand::execute() ----
4th
Main thread
4th
Main thread
---- EXIT CCommand::execute() ----
---- ENTER CCommand::execute() ----
5th
Main thread
5th
Main thread
5th
Main thread
5th
Main thread
5th
Main thread
---- EXIT CCommand::execute() ----
---- ENTER CCommand::execute() ----
6th
Main thread
6th
Main thread
6th
---- EXIT CCommand::execute() ----