LoginSignup
1
2

More than 5 years have passed since last update.

C++ 自作マルチスレッドライブラリ

Last updated at Posted at 2019-03-01

C++ 自作マルチスレッドライブラリ

昔会社で利用していたC++のマルチスレッドライブラリを自作してみました。以下で構成されています。
- Javaライクなスレッドライブラリ
- CommandDispatcher(EventDispatcher) パターン (マルチスレッドデザインパターン)

実装

https://github.com/tsuyuzaki/cpp/tree/master/MultiThreadCode
(本来はヘッダをincludeディレクトリに分けるなどすべきですがべたにコードを並べてしまっています。)

Javaライクなスレッドライブラリ

Javaのスレッドライブラリの機能を最小限にして模倣したなんちゃってスレッドライブラリ説明。

CThreadクラス

CThreadクラスはインスタンスごとにスレッドを起動します。
起動したスレッドでコンストラクタの引数で渡されたIRunnableのrun()メソッドをcallします。

Publicメソッド

CThread.h
    /**
     * CThreadのインスタンスを生成します。
     * 引数で渡したrunnableのrun()メソッドを別スレッドでcallします。
     * インスタンスの生成に失敗すると 0 が返ります。
     */
    static CThread *createInstance(
        IRunnable *runnable);

    /**
     * 新しいスレッドを起動します。
     * 1インスタンスで複数回呼び出すとassertします。
     */
    bool start();

    /**
     * 起動したスレッドの完了を待ち合わせます。
     * スレッドを起動したのち、呼び出しが可能です。
     * 1インスタンスで複数回呼び出すとassertします。
     */
    void join();

IRunnableクラス

CThread::createInstance()に渡すインスタンスが継承するインターフェースです。
IRunnableを継承したクラスのrun()メソッドに別スレッドで処理したい実装を定義します。

Publicメソッド

IRunnable.h
    /**
     * CThreadクラスのstartメソッドで起動したスレッドで実行されるメソッドです。
     */
    virtual void run() = 0;

CSynchronizerクラス

スレッドの排他を行うためのクラスです。

Publicメソッド

CSynchronizer.h
    /**
     * 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メソッド

CSynchronized.h
    /**
     * CSynchronizerのwait()を呼び出します。
     */
    void wait();

    /**
     * CSynchronizerのnotifyAll()を呼び出します。
     */
    void notifyAll();

CommandDispatcher(EventDispatcher) パターン

CCommandDispatcherクラス

ICommandをキューイングし、ワーカースレッドでICommandのexecute()を逐次実行するクラスです。
ワーカースレッド数は1です。
キューイングしたICommandはCCommandDispatcherがdeleteします。クライアント側でdeleteしないでください。

Publicメソッド

CCommandDispatcher.h
    /**
     * 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メソッド

ICommand.h
    /**
     * CCommandDispatcherのワーカースレッドで呼び出されるメソッド。
     */
    virtual void execute() = 0;

利用例

main.cpp
/**
 * コンストラクタの引数に渡した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() ----

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2