CodeZine(コードジン)

特集ページ一覧

POCO::Foundationでデザインパターン - マルチスレッド編 -

オープンソースC++用クラスライブラリPOCO活用講座(5)

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2008/07/04 14:00
目次

サンプルコードの解説

WorkNotificationクラス

 Poco::Notificationを継承したWorkNotificationは、ワーカースレッドへのメッセージを示すクラスです。内部ではコマンド(仕事)の処理も記述しています。サンプルではコマンド処理を1種類しか定義していませんが、通常のプログラムでは複数のコマンドを用意することが多いでしょう。その場合は、execute()を仮想関数としてクラスを継承し、各コマンド処理をオーバーライドして記述するようにします。

 Poco::Randomクラスは、疑似乱数を生成するクラスです。Park-Millerのアルゴリズムを使っているので、質的な問題があると言われているランタイム関数のrnd関数より、一様な乱数を生成すると思われます。

 サンプルコードのコマンド処理は、乱数で求めた時間だけsleepを行う、というものです。

Workerクラス

 ワーカースレッドクラスです。通知キューから通知オブジェクトを取得し、通知に記述してある処理を呼びだします。通知キューが空の場合は、取得できるまで待機(ブロッキング)します。この処理を無限ループで繰り返すようにしています。ただし、待機解除命令(後述)が出されると待機から制御が戻り、そこで終了となります。

 以下の、run()関数の最初の部分を見てください。

run()関数の抜粋
Poco::AutoPtr<Poco::Notification> pNf(_queue.waitDequeueNotification());

 これは、わかりやすく書くと次の記述と同値です。元の記述は、Poco::AutoPtrクラスにより、解放処理を省いているのです。

上記を書き換えたもの
    Poco::Notification*  pNf = _queue.waitDequeueNotification();
    if ( pNf ) {
       (省略)
    }
    else
        break;

    delete pNf;

 waitDequeueNotification()で、キューからの通知オブジェクトの取り出しとデータ待機を行っています。キューが空の場合、処理はブロッキングされているのですが、外部のスレッドから待機解除命令が出されると、待機をやめてNULLを返します。待機解除命令は、Poco::NotificationQueueクラスのwakeUpAll()関数によって行います。なお、wakeUpAll()関数はThreadPoolクラスで管理している全スレッドに対しての命令となります。

 通知オブジェクトを取得した後は、自分のスレッド名と通知オブジェクトの番号をコンソールに出力します。なお、出力処理を行う際は、同期オブジェクトMutexを使ったPoco::FastMutexを用いて排他ロックをかけています。これは、複数のスレッドからの同時コンソール出力によって、表示が乱れてしまうことを防ぐためです。具体的には、Poco::FastMutexをstaticなメンバ変数として、各スレッド間の排他処理に利用しています。また、Poco::FastMutex::ScopedLockを使うことにより、そのロックオブジェクトが生存している{}のスコープに限定した排他制御になっています。

 最後に、execute()でコマンドの実行を行っています。

メインスレッド

 _tmainに記述したメインスレッドの処理はコメントを追っていけば理解できると思いますが、少し補足しておきます。Poco::ThreadPoolクラスのコンストラクタでは、保持する最小のスレッド数、最大のスレッド数、待機時間を指定できます。デフォルトでは、次のようになっています。

ThreadPoolクラスのコンストラクタ定義
ThreadPool(
    int minCapacity = 2,
    int maxCapacity = 16,
    int idleTime = 60
);
maxCapacity

 最大数を超えてワーカースレッドをstart()関数で登録しようとすると、例外が発生します。最大スレッド数は、addCapacity(int n)関数で後から増やすこともできます。

idleTime

 スレッドが待機中になってからidleTime秒を超えた場合、ThreadPoolクラスはそのスレッドを強制的に終了させます。ただし、指定した最小数より保持しているスレッドが少なくなることはありません。

 スレッドの終了処理は、まず全ワーカースレッドへ終了指示を送り、その後全てのスレッドが終了するまで待機する、という「2段階の終了」となっています。このパターンのことを、「Two-Phase Termination」パターンと呼びます。

 ThreadPoolクラスの詳細については、Webサイトのドキュメントを参照してください。

シーケンス図

 Poco::NotificationQueueに対するPoco::Notificationオブジェクトの追加・取得処理と、Workerクラスでのexecute()処理の部分をシーケンス図にしてみました。

シーケンス図
シーケンス図

実行画面

 サンプルコードを実行すると、次のように50行分の表示が行われます。3つのワーカースレッドで、50個の仕事を手分けして処理している様子がイメージできるでしょうか。

実行画面
Worker-1 excute No.0
Worker-3 excute No.1
~(省略)~
Worker-2 excute No.49

さらに

 サンプルに手を加えて、ワーカースレッドを増やしてみてください。大抵の環境では、処理が早くなるはずです。

まとめ

 今回は、スレッド関連のクラスをちょっと駆け足で紹介しました。マルチスレッドはかなり奥の深い技術ですし、POCOのクラスも非常に多くの機能をサポートしています。限られた紙面ではとても全てを紹介しきれませんので、興味を持たれた方はぜひ原文のドキュメントやライブラリのソースを参考にして、活用してみてください。

参考資料

  1. 増補改訂版Java言語で学ぶデザインパターン入門マルチスレッド編』結城浩 著、ソフトバンク クリエイティブ、2006年3月
  2. POCO C++ Libraries Reference


  • LINEで送る
  • このエントリーをはてなブックマークに追加

バックナンバー

連載:オープンソースC++用クラスライブラリPOCO活用講座

著者プロフィール

  • WINGSプロジェクト 高江 賢(タカエ ケン)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XM...

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5