今回はqueについて概略を書いてみたいと思います。
que ・・と短縮して書いてしまっていますが、英語では以下のような意味があります。
queue:(順番を待つ人や乗り物の)列、(昔の男性のつけた)弁髪、おさげ
自分で細かく書くのは非生産なのでqueにつてはwikiを参照ください。LabVIEWのプログラムにおいてもqueueはvi.lib内に標準関数として機能が備わっており、同じような機能を持っています。
wikiに説明がある通り、データが先入れ先出しされるため、FIFO(First In First Out)とも呼ばれたりします。基本的な実装、動きは以下の動画を御覧頂ければ御理解頂けると思います。
このダイアグラムにはqueだけではなくnotifyやイベント処理も含まれてますが、動画中で〇で囲まれた部分がqueに関する処理になります。queを利用する際の流れとしては以下が基本となると思います。
- queをデータタイプを指定して作成する(obtain)
- queに生産者がデータを追加する(enqueue)
- queから消費者がデータを取り出す(dequeue)
- queを破棄する(dispose)
このようにqueを用いて生産者と消費者の処理を分けることの目的はどこにあると思いますか?・・・答えは以下の通りです。
- queはobtainする際に蓄積できる量(この例ではデフォルトのままなので-1=メモリ許容まで)までデータを蓄積することができる(データ蓄積量の制御)
- 生産者の処理と消費者の処理が並列処理になっているため、生産者の処理は消費者の処理速度に影響を受けない(処理安定性の向上)
- 生産者がデータを追加しているときでも消費者はメモリからデータを取り出すことができる(並列処理による処理速度の向上)
queを用いることで、これらの効果を一度に受けることができる点が特筆すべきだと思います。ある程度データが蓄積されたらファイルに書き込む(データ蓄積量の制御)、受信したデータをデータベースサーバへ書き込みたいがサーバの処理速度が不安定なとき(処理安定性の向上)、DAQカードからデータを読み込み(生産者)しつつ平行してファイルへ書き込みを実行する(並列処理による処理速度の向上)などのプログラム作成上不可欠な効用があります。
またqueにはさらに違った使い方もありまして、使いこなすことでこちらも非常に有効なプログラム手法となります。
- dequeueをwhile loopのタイミング処理に用いる
- 通常while loopなどで並列処理を追加すると繰り返し実行する周期をtimer、timed loopなどで実装する必要があるのですが、この周期処理をqueの受信待ち(dequeue)で代用することが可能です。要は「queから何か得られたら処理を実行する、timeoutならばsleepする」ということになります。
- queueのサイズを1として作成し、単一メモリ(or Functional Global)のように用いる
- queサイズを1個で作成すると、必然的にそのqueが読みだされるまでは別な並列処理が書き込みを行おうとしても実行することができません。この原理を逆手にとると、queは書き込み制限付きのメモリと考えることができます。NIのiniファイルの処理がこの方法で実装されていますので興味がありましたら中身を探索してみてください。私はこの処理を見たときに最初意味が理解できませんでしたが、じっくり考えて理解できたときに鳥肌が立ちました(その前のNIのiniファイル処理が雑、バグすぎて自前で作らざるを得なかったのは内緒です)。
- queをステート処理のコマンド送信用として用いる
- プログラムを作成する際、どのような構造でプログラムを作るか?は一番重要な命題だと思います。作ったあとの容易な拡張性の確保、メンテナンス効率、コードの一貫性などを考慮すると、ステートフローという実装法で実装することが解だと私は理解しました。queはステート処理でのフローを明確、かつ容易かつ高速に管理できるアイテムとして利用することができます。このqueを用いたステート処理の実装法については後日お話するかもしれません(どなたか教えてくださいor議論しませんか?w)。
このようにqueueを用いることで得られる多くのメリットがあります。まだqueueを用いたことがない方がいらっしゃいましたらぜひ一度お試しください。新しいプログラム手法の可能性が開けると思います(私はそう思っています)。不明な点、誤りがあればいつでもコメント頂けると幸いです。SEOとかしたくないので(強がり)、とっても静かですw。