Queued Message Handlerをよりうまく使うには ~メッセージキューの破棄~

Queued Message Handler (QMH) のテンプレートでは、メッセージキューの作成はCreate All Message Queues.viというサブVIでまとめて作成しますが、キューを破棄するサブVIはありません。メッセージ処理ループ (Message Handling Loop、MHL)のExitケースでメッセージキューを破棄しています。

QMHのテンプレートのExitケース

個人的にはVariantをエラークラスタに変換するときにエラー配線していないのも気に食わない(Variantがデフォルト値の場合、Variant To Dataでエラーが発生するが、そのときにDataとしてエラーのデフォルト値=エラーなしが出力されるというもの)ですが、これはこういう仕様であると考えておいて、問題はExitケース内、あるいはWhileループ内でメッセージキューを破棄している点です。ここではこの問題点と、個人的にどう対応しているかを記述していきます。

何が問題か?

まず、一般的に、キューリファレンスやファイルリファレンスなど、作成、あるいはオープンが必要となるリファレンスは、いったん作ったら可能な限り使いまわし、使い終わったら破棄するものです。これはリソースの作成や破棄が比較的時間のかかる処理で、何度もリソースを使うならリファレンスを使いまわすのが効率的だからです。

QMHのテンプレートでは、各Whileループの前にあるCreate All Message Queues.viでメッセージキューを作成し、MHLのExitケースで破棄しています。一見すると、キューリファレンスをちゃんと使い回せているように見えますが、これはQMHのテンプレートではUIというメッセージキュー1つしかないから成り立ちます。メッセージキューを増やし、MHLを増やしていった場合はそう簡単にはいきません。なぜなら、各MHLでは自分以外のメッセージキューにエンキューすることがあるからです。なので、Exitケースでメッセージキューを破棄すると、ほかのMHLで破棄したメッセージキューにエンキューしようとしたときにエラーが発生します。こういうエラーは正常終了させる場合はあまり問題になりませんが、異常終了させる場合はどのメッセージキューが有効か確認しながらキューを破棄しないと、破棄したキューにエンキューをしてエラーが発生します。

QMHでMHLが4つある場合

上のブロックダイアグラムの場合、例えばUIと書いてあるサブVIにあるMHLでほかのMHLにExitを送るとします。正常終了ならそれでいいですが、例えばTESTのMHLで異常を検出してUIのMHLにExitを送ってTESTのMHLを終了する場合、TESTのMHLでキューを破棄すると、UIで全部のMHLにExitを送る際にTESTのメッセージキューは破棄済みのためエラーが発生します。Exit処理の作りによってはこのエラーによってほかのMHLにExitを送れない可能性があり、無限ループに陥る可能性があります。

それに、各MHLの外でメッセージキューを作るなら、破棄も各MHLの外でやるのが、なんというか対称性があるというか、対応関係があるというか、対等というか、個人的には適していると思っています。まあ、私個人の感覚的なものですが。

どうやってメッセージキューを破棄するか?

これは、「すべてのMHLのExitケースにあるRelease Queueを削除して、すべてのMHLを終了してからメッセージキューをまとめて破棄するのをおすすめします。」というので終わりです。…ですが、それだけだと短いので、もう少しいろいろ記述したいと思います。どうやるのが効率的かについてですね。こんなサブVIを作るといいです。

Close All Message Queues.vi

このサブVIを使う場合、MHLでメッセージキューを破棄すると破棄を2回行うことになるので、MHLのRelease Queue関数は削除しておきます。

MHLのExitからRelease Queueを削除する

かなりシンプルなサブVIです。このサブVIではMessage QueuesクラスタをCluster to Arrayで配列に変換しているので、メッセージキューの型はすべてのメッセージキューで同じである必要があります。メッセージに列挙体を使うとメッセージキューの型を合わせることができないので、メッセージに文字列を使うことを私はおすすめします。こうすると、いったんこのキュー破棄のサブVIを作れば、メッセージキューを追加してもこのサブVIを触ることなく追加した分も破棄できます。

QMHのLauncher VIにClose All Message Queues.viを置く

こんな感じで使うと、左でメッセージキューを作成し、真ん中の処理(すべてのサブVIにMHLがあり、UIにはイベント処理ループもいる)が全部終了したらメッセージキューをすべて破棄するという、初期化処理 → Main処理 → 終了処理という流れが把握しやすい(?)ものになります。…実際は初期化とメイン処理の間に構成のロードとかあるかもですし、それを含めて初期化というのかもしれませんが。真ん中の処理の後にMerge Errorを置いてエラー配線することで、全部のMHLが終了してからメッセージキューの破棄をします。こうすることで、真ん中の処理を行っている間はすべてのメッセージキューが生きていることを確信できます。こんな感じにすると、MHLが大量にあっても全体像を把握しやすくなるかもしれません。…個人差は大いにありですが。

まとめ

今回は、QMHでメッセージキューをどう破棄するかについて、私のやり方を紹介しました。個人的にはQMHのテンプレートでなんでMHLの中で破棄しているのかが謎なんですよね。外で作ってるんだから外で破棄したほうがいいのではないかと思っていますし、実際に私はそうしています。Exitケース内で破棄する理由があり、そっちのほうが合理的あるいは自分のプロジェクトに向いているという場合もあると思うので、ここの内容が参考になるところがあればそこだけをお持ち帰りいただければと思います。

Github

GitHub – taacranzk/TN_QMH_Modification: LabVIEWのQueued Message Handlerをベースに手を加えたものです。
LabVIEWのQueued Message Handlerをベースに手を加えたものです。. Contribute to taacranzk/TN_QMH_Modification development by creating an ac…
タイトルとURLをコピーしました