japan.internet.comThe Internet & IT Network
RSS
  • ニュース
  • コラム
  • リサーチ
  • ヘッドライン
  • 特集
  • ブログ
  • プレスリリース
  • 専門チャンネル
  • イベント
  • ランキング
  • ニュースメール
2009年7月4日
文字サイズ文字サイズ小文字サイズ中文字サイズ大
デベロッパー2005年10月4日 22:00

Windows のスレッドプーリング

海外海外internet.com発の記事
  • このエントリーを含むはてなブックマーク
  • この記事をクリップ!
  • Buzzurlにブックマーク
  • Yahoo!ブックマークに登録
  • newsing it!
  • この記事をokyuuへインポート

はじめに

 スレッドプーリングとは、複数の実行スレッドを管理し、処理を各スレッドに分散させるためのテクニックである。場合によっては、同時実行制御などの機能も、スレッドプーリングの一部と考えられる。スレッドプーリングは、以下の処理を作業を行うのに適した方法である。

複雑な処理を管理する

 スレッドプーリングはステートベースの処理に適している。システムをいくつかのステートマシンに分解できる場合には、スレッドプーリングを利用してそのシステムを効果的に実現することができる。これには、たいていの場合にマルチスレッドアプリケーションのデバッグが単純化されるという副次的なメリットもある。

アプリケーションの拡張性を高める

 正しく実装すれば、スレッドプールによって同時実行の制限を設け、アプリケーションの拡張性を高めることができる。

リスクを最小限に抑えながら新規コードを追加する

 スレッドプーリングを利用すると、"サンドボックス"と呼ばれる、システムの実行をいくつかの小さな単位に分割するセキュリティモデルを実現できる。"サンドボックス"モデルでは、スレッドプーリングによってプロセス間の結び付きが緩やかになり、プロセスからデータが独立するため安全である。この方式では、プロセスどうしが複雑にからみ合うのではなく、点で接することになる。これにより、保守の手間が大幅に減少する。特に、大規模なマルチスレッドアプリケーションでは効果が大きい。

設計

 スレッドプールの概念モデルは単純で、

  1. スレッドプールがスレッドを開始する
  2. 処理がスレッドプールのキューに入れられる
  3. キュー内の処理が使用可能スレッドで実行される

 という形である。テンプレートを使用すると、プールをスレッド/処理の実装から独立させることができる(このテクニックは静的ポリモーフィズムと呼ばれる)。

図1 スレッドプールのコラボレーション図
図1 スレッドプールのコラボレーション図

 スレッドプールはスレッドの作成を担当する。スレッドはworker::thread_procによって実行を開始する。リクエストはスレッドプールのキューに入れられる。このとき、ワーカーがリクエストの準備をし、リクエストをキューに入れる。スレッドが処理を実行できる状態になったときは、スレッドがthread_pool::get_queued_statusを使用して、スレッドプールに待機中の処理をリクエストする。待機中の処理がない場合は、処理が送信されてくるまで、スレッドは休止状態になる。

チェイニング

 このワーカー実装によって処理をキュー化できるが、さらに一歩進んでみよう。このスレッドプールは問題を個別のステップに分解するのに役に立ち、それによってシステムの能力を最大限に活用しつつ、複雑さとリスクを最小限に抑えることができる。しかし、現在の実装では一度に1つの処理しかキュー化できないので、一連の処理を論理的にまとめるのが難しい。さらに、後続の処理をキュー化するためには、処理がいつ実行されたかを知る手段が必要になる。

具体的な例

 システムをダウンさせ、システムデータを再構築し、システムを再びオンラインにする、という一連の処理がある。これは3つのステップ、3つの処理である。これを次のような形で実装したいと思う。

thread_pool::instance().queue_request(
   (core::chain(), new system_down, new rebuild_data,
    new system_up));

 ここで出てきたチェイン(chain)とは、一種の処理単位のことである。正確には、このチェインは、実際の処理単位(実は処理単位のコンテナ)のコンテナとしてのみ機能する。

struct chain {
struct data : work_unit, std::list<smart_pointer<work_unit> > {
void process();
};

chain() : m_work(new data) {}
chain& operator,(work_unit* p_work);
operator work_unit*() { return m_work; }
smart_pointer<data> m_work;
};    // struct chain

 chain::operatorは、次の処理だけを行う。

m_work->push_back(p_work);
return *this;

 chain::data::process()は、次の処理だけを行う。

front()->process();
pop_front();

// if not empty, requeue
if (true == empty()) return;
thread_pool<worker_thread>::instance().queue_request(this);

コードの使用方法

 まず、使用するスレッドプールを初期化する。スレッドプールはパラメータ付きのシングルトンなので、使用するワーカーの種類ごとに1つのスレッドプールインスタンスが存在する。global::thread_poolクラスは、core::thread_pool<core::worker_thread>を表す便利なtypedefである。

global::thread_pool::instance().initialize();

 core::worker_threadをワーカー実装として使用する場合は、すべての処理がcore::work_unitから派生することになり、このプロセスが呼び出されたときに処理が実行される。

struct mywork : core::work_unit
{
   void process() throw()
   {
      // work is processed here
   }
}

 処理をキュー化するには、クラスのインスタンスを作成し、必要に応じて初期化する。この処理を、thread_pool::queue_requestを使用してキュー化する。

// demonstrate chaining
global::thread_pool::instance().queue_request(
   (core::chain(), new work_1, new work_2, new work_3));

 スレッドプールをシャットダウンするには、thread_pool::shutdownを使用する。

global::thread_pool::instance().shutdown();

 他のスレッドプールを作成するには、ワーカースレッドとその構成部品を定義すればよい。ワーカースレッドでは、request_typeprepare_requestthread_proc必ず定義する必要がある。thread_procに渡されるLPVOIDパラメータは必ず0で、使用してはならない。このパラメータを通じてコンテキストを提供するようスレッドプールを拡張することができる。次のコードでは、io::threadio::thread_poolを定義している。

   // sample io worker thread
   namespace io {
   struct thread
   {
      typedef io::session request_type;
      static void prepare_request(request_type*) throw();
      static void thread_proc(LPVOID) throw();
   };    // struct thread

   struct thread_pool : core::thread_pool<io::thread> {};
   }     // namespace io

 ユーザーはio::thread_pool::instance();を使用してio::thread_poolにアクセスできる。

デモプログラムについて

 デモプログラムは次のことを行う。

  • global::thread_poolインスタンスの初期化
  • 3種類の処理のインスタンス化
  • 処理をまとめるチェインのインスタンス化
  • 処理のキュー化と実行
  • スレッドプールのシャットダウン

 チームで開発を行っている場合は、大きな処理を小さな処理単位に簡単に分割し、チーム内で分配することができる。それぞれの処理単位は独立してテストでき、かつ、最終的な製品として統合もできる。それぞれの開発者は、担当範囲内で自分の仕事をまっとうすればよいのだ。

 スレッドプールは、拡張性とパフォーマンスに優れた大規模システムを短期間で安全に開発するための素晴らしいツールである。

 ぜひ試していただきたい。

著者紹介

Joshua Emele(Joshua Emele)
サンフランシスコ在住。Plugware Solutions, Ltd. に勤務。C++ でのネットワーク、データベース、ワークフロー アプリケーション開発を専門とする。人生とパートナーをこよなく愛し、クラシック ギター、ハイキング、デジタル機器を好む。

Plugware Solutions, Ltd. ― 設計、レビュー、統合、実装に関するコンサルティング サービスを業務とする。Plugware Web Services Platform の開発元。
このエントリーを含むはてなブックマーク この記事をクリップ!
BuzzurlにブックマークBuzzurlにブックマーク Yahoo!ブックマークに登録
この記事をokyuuへインポート
最新トップニュース
データメーション
【データメーション】
中国が「Green Dam」フィルタ規制を撤回(7月1日)
Graphic Design Forum
【Graphic Design Forum】
Chris Dickman(6月25日)
プライバシー ジャパン・インターネットコム版
【プライバシー ジャパン・インターネットコム版】
グーグル・ストリートビューの問題について総務省の見解(6月23日)
エンジニアの独り言
【エンジニアの独り言】
システムを「使う」時代のエンジニアに求められるもの(6月2日)
最新ハイテク講座
最新ハイテク講座
電気は家庭でつくる時代へ!燃料電池「エネファーム」(7月3日)
アクセス解析で見るWebマーケティング
アクセス解析で見るWebマーケティング
決定力を探るアクセス解析(7月3日)
百式のネットビジネス研究
百式のネットビジネス研究
ファーストフードを高級っぽく盛り付けて紹介している「Fancy Fast Food」(7月3日)
週刊-サイト別アクセス状況データ
週刊-サイト別アクセス状況データ
ビデオリサーチインタラクティブ調査(月間インターネットオーディエンスデータ)(7月2日)
成約率、反応率を上げる Web 文章術
成約率、反応率を上げる Web 文章術
言葉がダイレクトにキャッシュを生む(7月2日)
不況時代の Web ビジネス最適化講座
不況時代の Web ビジネス最適化講座
アクセス解析エキスパートここだけの話、Web コンシェルジュの“勉強法”こっそり教えます(7月2日)
「Webからの脅威」―その傾向と最新対策
「Webからの脅威」―その傾向と最新対策
不正プログラムの分類(7月1日)
DevX
DevX
JavaScriptとDOMによる動的なWebページの作成(6月30日)
エンジニア転職ノウハウ開発室
エンジニア転職ノウハウ開発室
今のままで大丈夫?3匹の子ブタ的キャリア危険度診断(6月30日)
アイレップの SEM フロンティア
アイレップの SEM フロンティア
Web サイトは「無駄な穴のたくさん開いたじょうご」〜サイト成果向上の基本的な考え方(6月30日)
Copyright 2009 Japan Internet.com K.K. All Rights Reserved.http://www.internet.com/