4

デフォルトでノンブロッキング I/O を備えた Qt フレームワークを使用して、複数の Web ページ (オンライン ストア) をナビゲートし、これらのページでさまざまなアクションを実行するアプリケーションを開発しています。特定の Web ページを、このページをナビゲートするために使用するステート マシンに "マッピング" しています。
このステート マシンには次の遷移があります。
Connect, LogIn, Query, LogOut, Disconnect
そしてこれらの州。
Start, Connecting, Connected, LoggingIn, LoggedIn, Querying, QueryDone, LoggingOut, LoggedOut, Disconnecting, Disconnected
*ing 状態から *ed 状態への移行 ( Connecting->Connected) は、LoadFinished現在要求されている URL が読み込まれたときにネットワーク オブジェクトから受信した非同期ネットワーク イベントによるものです。*ed から *ing 状態 ( Connected->LoggingIn) への遷移は、私が送信したイベントによるものです。
このマシンにいくつかのイベント (コマンド) を送信できるようにしたい (Connect、LogIn、Query("productA")、Query("productB")、LogOut、LogIn、Query("productC")、一度に処理してもらいます。マシンに送信したすべてのイベントの処理が完了するのを待つことをブロックしたくありません。問題は、ダウンロードされている URL についてマシンに通知する上記のネットワーク イベントとインターリーブする必要があることです。*ing から *ed への進行は、ネットワーク タイプのイベントを受信した後にのみ行われるため、インターリーブがないと、マシンはその状態を進める (およびイベントを処理する) ことができません。

どうすれば設計目標を達成できますか?

編集

  1. 私が使用しているステート マシンには独自のイベント ループがあり、イベントはキューに入れられないため、マシンがビジー状態のときにイベントが発生すると、マシンによって見逃される可能性があります。
  2. ネットワーク I/O イベントは、使用しているステート マシンにもイベント キューにも直接ポストされません。それらは私のコード (ハンドラー) に投稿され、私はそれらを処理する必要があります。希望通りに転送できますが、備考欄にご注意ください。1.
  3. 現在の設計を詳細に説明したこの質問に対する私の回答を見てください。問題は、このデザインを作成することで、このデザインを改善できるかどうか、またどのように改善できるかです。

    • より堅牢
    • よりシンプルに
4

6 に答える 6

6

ステート マシンにイベント キューが必要なようです。イベントをキューに入れ、最初のイベントの処理を開始し、それが完了したら次のイベントをキューから取り出して開始します。そのため、ステート マシンはクライアント コードによって直接駆動されるのではなく、キューによって駆動されます。

つまり、1 つの遷移の結果を次の遷移で使用するロジックはすべて、マシン内に存在する必要があります。たとえば、「ログイン完了」ページが次に進むべき場所を示している場合。それが不可能な場合、イベントにはマシンが呼び出すことができるコールバックが含まれている可能性があります。

于 2009-08-12T10:39:51.107 に答える
2

この質問をすることで、回答をどの方向にも歪めないように書きたくない実用的なデザインがすでにありました:)この疑似回答で、私が持っているデザインが何であるかを説明します。

ステート マシンに加えて、イベントのキューがあります。イベントをマシンに直接投稿する代わりに、イベントをキューに入れています。ただし、非同期でいつでも発生するネットワーク イベントには問題があります。キューが空ではなく、ネットワーク イベントが発生した場合、既にキューにあるイベントを処理する前にマシンが待機状態になるため、ネットワーク イベントをキューに配置できません。このネットワーク イベントは、以前にキューに配置されたすべてのイベントの背後で待機しているため、マシンは永遠に待機します。
この問題を克服するために、2 種類のメッセージがあります。通常のものと優先のもの。通常のものは私が送信したもので、優先のものはすべてネットワークのものです。ネットワークイベントを取得すると、それをキューに入れずに、マシンに直接送信します。このようにして、イベントのキューから次のイベントを取得する前に、現在のタスクを終了して次の状態に進むことができます。
このように設計されているのは、イベントとネットワーク イベントが正確に 1:1 でインターリーブされているためです。このため、マシンがネットワークイベントを待っているときは何もしていません (したがって、それを受け入れる準備ができており、それを見逃すことはありません)。ネットワーク 1。

今あるものよりもっとシンプルなデザインを期待してこの質問をしました。

于 2009-08-12T11:48:45.630 に答える
1

厳密に言えば、できません。状態が「接続中」しかないため、後でトップログインが必要かどうかわかりません。Start 状態からの「Connect, then Login」イベントの結果を表すには、状態「ConnectingWithIntentToLogin」を導入する必要があります。

当然、「Connecting」状態と「ConnectingWithIntentToLogin」状態の間には多くの重複があります。これは、ステート階層をサポートするステート マシン アーキテクチャによって最も簡単に実現できます。

- - 編集 - -

その後の反応を読むと、実際の問題が何であるかが明らかになりました。

それがFSMに根付いているか、別のキューの外部にあるかに関係なく、明らかに追加の状態が必要です。追加のイベントをキューに入れて、好みのモデルに従ってみましょう。ここで問題なのは、キューに入れられたこれらのイベントをリアルタイム イベントに対して「インターリーブ」する方法を知りたいということです。そうではありません-特定の状態に入ると、キューからのイベントがアクティブに抽出されます。あなたの場合、それらは「接続済み」のような「* ed」状態になります。キューが空の場合にのみ、「接続済み」状態のままになります。

于 2009-08-12T10:37:53.777 に答える
0

バックグラウンドでブロックI/Oのリストを作成したいようです。

したがって、スレッドを実行させます。

while( !commands.empty() )
{
  command = command.pop_back();
  switch( command )
  {
  Connect: 
    DoBlockingConnect();
    break;
  ...
  }
}
NotifySenderDone();
于 2009-08-12T14:26:54.543 に答える
0

ブロックしたくない場合は、ネットワークの応答を気にしないことを意味します。一方、返信に興味がある場合は、それらを待ってブロックする必要があります. それ以外の方法で FSM を設計しようとすると、すぐにオートマトンのサイズが無限大になります。

于 2009-08-12T11:02:48.530 に答える
0

ステート マシンを別のスレッド、つまり QThread に移動するのはどうですか。ステート マシンに入力キューを実装して、クエリをブロックせずに送信し、出力キューを送信してクエリの結果を読み取ることができるようにします。クエリの結果が到着した場合、connect(...) を介してメイン スレッドでスロット関数をコールバックすることもできます。Qt はこの点でスレッド セーフです。

このようにして、ステート マシンは、メイン プログラムをブロックすることなく、必要な限りブロックできます。

于 2009-08-12T14:24:46.747 に答える