生産者/消費者デザインパターンは、マスタ/スレーブパターンに基づいており、異なるレートで実行する複数のループ間でのデータ共有の強化を目的としています。生産者/消費者デザインパターンは、データの生成速度と消費速度が異なる処理を分割します。生産者/消費者デザインパターンの並列ループは、データを生産するカテゴリと、生産されたデータを消費するカテゴリという2つのカテゴリに分類されます。
この記事では、生産者/消費者アーキテクチャの一般的な用途と利点に加え、この技術をLabVIEW内で使用するためのリソースを紹介しています。ループ間での情報共有の詳細や、LabVIEWで生産者/消費者ループを作成するためのステップバイステップガイドおよび演習については、LabVIEW 実践集中コース 2の受講を検討してください。
生産者/消費者パターンを使用すると、個々のレートで反復しながら、同時に複数のプロセスを簡単に処理することができます。
異なる速度で実行している複数のプロセスがある場合、プロセス間のバッファ型通信は非常に効果的です。バッファが十分に大きいと、生産者ループは、データ損失なしに消費者ループより高速で動作できます。
たとえば、アプリケーションに2つのプロセスがあり、1番目のプロセスはデータ収集を実行し、2番目のプロセスはそのデータを取得してネットワークに配置するとします。最初のプロセスは2番目のプロセスの3倍の速度で動作します。このアプリケーションを実装するために生産者/消費者デザインパターンを使用した場合、データ収集プロセスは生産者として機能し、ネットワークプロセスは消費者として機能します。十分な大きさの通信キュー(バッファ)があれば、ネットワークプロセスは、データ収集ループが収集する大量のデータにアクセスできます。データをバッファするこの機能により、データ損失が最小限に抑えられます。
キュー機能を使用した場合に発生するバッファ型通信を可視化するには、サンプルプログラムの「Move LabVIEW Window Using Producer/Consumer Loops」をご覧ください。
生産者/消費者パターンは一般的に、複数のデータセットを収集して順番に処理する場合に使用されます。
たとえば、データを受け入れ、受け取った順番でデータを処理するアプリケーションを作成するとします。このデータをキューに入れる(生産)は実際の処理(消費)よりはるかに速いので、生産者/消費者デザインパターンはこのアプリケーションに最適です。これにより、消費者ループは独自のペースでデータを処理することができ、生産者ループは同時に追加データをキューに追加することができます。
このアプリケーションで生産者と消費者が同じループ内にあると考えた場合、データ収集スピードはデータ処理スピードに合わせなければならないため遅くなります。 これが、データ収集 (生産者) と処理 (消費者) というプロセスに、プログラムを分割することが役に立つ理由です。
ネットワーク通信では、2つのプロセスを異なるスピードで同時に処理することが求められます。1番目のプロセスでは絶えずネットワークラインをポーリングし、パケットを回収します。2番目のプロセスでは1番目のプロセスで回収したパケットを受け取り、解析します。この例においては、最初のプロセスが2番目のプロセスにデータを要求するため、最初のプロセスが生産者として機能し、2番目のプロセスが消費者として機能します。このアプリケーションは、生産者/消費者デザインパターンを使用することに利点があります。並列する生産者ループと消費者ループが、データの回収と解析をネットワークから切り離して処理します。また、2つのループ間で、通信を待ち行列化して行うため、回収したネットワークパケットのバッファリングが可能になります。ネットワーク通信がビジー状態になると、このバッファリングが非常に重要になります。バッファリングを行うことにより、パケットの回収と通信が、解析よりも高速に実行できます。
キューメッセージハンドラアーキテクチャは生産者/消費者アーキテクチャの特別バージョンです。データキューは、生産者/消費者デザインパターンにおけるループ間のデータ通信に使われます。キューには、生産者と消費者のループ間のデータバッファリングという利点があります。
生産者/消費者デザインは生産者と消費者という2つのカテゴリに分類される並列ループで構成されます。生産者ループと消費者ループの間の通信は、キューまたはチャンネルワイヤを使用して行われます。
LabVIEWにはキュー操作VIが組み込まれており、このキュー操作VIには機能パレット→データ通信→キュー操作からアクセスできます。
キューは先入れ/先出し理論に基づいています。生産者/消費者デザインパターンでは、キューを生産者ループと消費者ループの外で初期化することができます。生産者ループは消費者ループのためにデータを生成するので、キューにデータを追加します(キューにデータを追加することを「エンキュー」といいます)。
消費者ループはそのキューからデータを削除します(キューからデータを削除することを「デキュー」といいます)。キューは先入れ/先出しであるため、データは、常に生産者がキューに追加したのと同じ順番で消費者によって解析されます。図1は、生産者/消費者デザインパターンをLabVIEWで作成する方法を示しています。
図1.生産者/消費者デザインパターン
LabVIEWでキューを使用するためのサンプルが用意されており、ご自身のアプリケーションの開始点として使用することができます。サンプルを見つけるには、LabVIEWサンプルファインダを使って、キューを検索してください。
チャンネルワイヤ機能はLabVIEW 2016で追加されました。チャンネルワイヤを使ってキューと同じ機能を実現できます。
キューでは、キューリファレンスのセットアップ (キュー取得)、データの追加 (エンキュー)、データの削除 (デキュー) およびキューリファレンスのクローズ (キュー解放) を行います。チャンネルでは、このプロセスが簡易化され、データのライタとリーダをセットアップするだけで済みます。
チャンネルワイヤおよびスタートアップテンプレートの詳細については、LabVIEWヘルプの「チャンネルワイヤを使用して並列したコードセクションの間でデータを通信する」をご覧ください。
キューや同期など、生産者/消費者デザインパターンを扱う際に注意すべき点がいくつかあります。
問題: キューは1つの特定のデータタイプにバインドされています。したがって、生産者ループで生成される異なるデータ項目ごとに、異なるキューを使用する必要があります。これはブロックダイアグラムが複雑化するため、問題になる可能性があります。
解決策: キューは、配列やクラスタなどのデータタイプを受け入れることができます。各データ項目はクラスタ内に配置できます。これにより、様々なデータタイプはクラスタデータタイプの背後に隠れます。図1では、通信キューにクラスタデータタイプが実装されています。
問題: 生産者/消費者デザインパターンは同期に基づいていないため、ループの最初の実行は特定の順番に従いません。 したがって、あるループをその他のループの前に初期化すると、問題が発生する可能性があります。
解決策: 生産者/消費者デザインパターンにイベントストラクチャを追加することで、この種の同期の問題を解決できます。 図2は、この機能を実現するためのテンプレートを示しています。 同期関数に関する詳細な情報は、関連リンクセクションの下にあります。