質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.11%

複数機器を制御するアプリケーションのマルチスレッド設計について

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 2,849
退会済みユーザー

退会済みユーザー

複数機器を制御するアプリケーションのマルチスレッド設計について

いつもお世話になります。

主題の件について、長文になってしまいますが、ご意見・ご教授をお願い致します。

 前提条件

 システム要件

例として、以下の図のようなイメージ構成のシステムを考えます。
今回作成するアプリケーションはソフトウェアFOOに該当します。

イメージ説明

このシステムは、以下の動作を行います。

  • FOOは、複数のマシンを制御するアプリケーションである
  • 各マシンは非同期的に動作を行う
  • FOOは各マシンと通信にて制御を行う
  • FOOは基本的にはサーバ的な役割を行う(マシンから通知が来たら、何かしらの処理をして返す)
  • ただし、FOOから能動的に、各マシンへ動作を指示する場合もある  
    (例えば、停電発生時に各機器への縮退指示など)
  • 図にはないですが、FOO内の各コントローラは、例えば「電源状態」とか「システムスケジュール」といった共通のステータスを参照する
  • 図にはないですが、マシンAが動いているときは、マシンBは停止するといった、マシン間の制御も行う必要がある
 開発環境
  • C#を使用した.NETアプリケーション
 現状のソフトウェア設計
  • 各コントローラは1つのコントローラクラスのインスタンスとして、Factoryクラスから生成する

 悩んでいる点

ここで、「マシンAのコントローラ」等の、各コントローラでどのように処理を実現すべきか悩んでいます。
具体的に、各コントローラはそれぞれ非同期(ときには同期)で処理を行うことになるのですが、以下の2パターンを構想しています。

<構想1>

  • コントローラクラスを常駐スレッド化する  
    →つまり、各コントローラそれぞれに常駐スレッドを設ける
  • インフラストラクチャー層とはスレッド間メッセージキュー等でやり取りして、各マシンの制御を行う

<構想2>

  • コントローラクラスは常駐スレッドを持たない
  • マシンからメッセージを受け取ると、インフラストラクチャー層からコールバックでコントローラへ通知を行い、それをきっかけとして非同期処理を開始し、処理が終わると非同期処理を終了する。  
    (イメージとしては、C#でいうと、Task.Run()で非同期処理を行い、各コントローラごとの常駐スレッドはなしで処理を行う)

つまり、構想1・2において、「常駐スレッドをたてる」か「通知が来たときのみ非同期処理を行う」かの違いがあります。

それぞれ、以下の特徴があると考えています。

  • 構想1は、コントローラインスタンスというオブジェクトが、メッセージを受け取ると能動的に処理を行うという点において、オブジェクト指向っぽいやり方である。  
    ただし、複数スレッドが常駐するため、全体としての制御が難しい?
  • 構想2は、必要な時に必要なタイミングでマルチスレッドで動作を行うので、スレッドの資源を有効的に使える。
    そして、C#ではマルチスレッド処理はTaskを使用するのが一般的と聞いたので、C#で実現するならこちらのほうが推奨されている?

 <補足>ソフトウェアに要求される性能

ソフトウェア的には、パフォーマンスはそこまで要求されません。
どちらかというとメンテナンス性(変更容易性や可読性など)が要求されます。

 質問点

  • 上記の構想1・2において、どちらを選択すべきでしょうか。(または他に選択すべき方法があるでしょうか)  
    →どちらがメンテナンス性に優れているでしょうか。
  • どちらのやり方が一般的なのでしょうか。

個人的な考えとしては、C#の時流がTaskによる非同期処理なので、構想2が妥当なのかなと思いますが、メンテナンス性や可読性は構想1の方が優れているのかなと感じています。

また、以下の点においてもご意見頂ければ助かります。

  • そもそも、それぞれの構想の特徴の捉え方は間違っていないでしょうか。
  • もし同じような経験をされた方がおられましたら、アドバイス頂けますでしょうか。

以上、長くなりましたが、よろしくお願い致します。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

こんにちは。

マシンとの通信が状態を持たない単発の1往復で完了し、次の単発との関連がない場合は「構想2」で全体の構造を単純に保つのは好ましいと思います。

しかし、もし、例えば「マシンからA要求がきたら、a応答を返しそのままB要求をサーバ側から送って...」みたいな複雑な通信処理が必要になるなら、その「シーケンス」1つ毎に「スレッド」を割り当てて同期処理した方が見通しはよくなります。

その「シーケンス」がせいぜい2~3ステップ程度の単純なものなら、async関数で実装し、各ステップをawait Task.Runで実装すると楽できます。await Task.Runの前後でコントロールを触れるからなかなか楽です。(例外がApplication.ThreadExceptionに届かないのでちょっとアレですが。)

しかし、async関数はたいへん残念なことに非asyncなメソッドを呼び出してそのメソッドの中でawaitできないのであまり大きな処理を作ることはお勧めしません。(内部で通信するようなメソッド呼び出しを使わないで書いても問題のない程度の処理でないと厳しいです。)

通信制御がそれ以上に複雑になる場合は、構想1のように常駐スレッドを起こすのが良いだろうと感じます。

複数スレッドが常駐するため、全体としての制御が難しい?

常駐スレッドの場合、そのスレッドを終了させる機能が必要になります。きちんと適切に設計できれば簡単なのですが、意外にはまります。マイクロソフトは同期式I/Oに中断機能を設けないことが多いからです。中断させるためだけに非同期I/Oにしないといけないことも度々あります。
VistaでやっとまともにサポートされたI/Oのキャンセル機能

長時間稼働しないスレッドしかないなら、キャンセル処理しないのも選択肢に入るので手間を減らせます。アプリ終了時もちょっと待っている内に終了するのですから。

あとTaskを使う場合は複数のTaskを一度に開始しようとすると遅延が発生するを見ておくと良いかも知れません。
私はこれにはまりました。スレッド起動に数秒かかるとか普通は有り得ないので意外にはまります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/02 15:16

    回答ありがとうございます。

    やはり構想2のほうが構造が単純になるのですね。ここら辺、もともとがC言語上がりなので実際に作ってみないとイメージがわかないのでしょうが、とりあえずは構想2で作っていき、限界を感じたら構想1に切り替えるというつもりでやっていこうと思います。
    また、Taskの起動時間に関する情報もありがとうございました。今回のシステムでは例えば同時に10スレッドを生成するということがあるかもしれないので、その際は動作に注視したいと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.11%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問