2

Indy 10 TCPコマンドハンドラーを使用して、コマンドを取得するたびに、データベースに行を挿入し、データベースから物語全体を読み取って文字列グリッドを更新します。

私はAnyDacデータベースコンポーネントを使用していますが、それらのドキュメントには、「接続オブジェクトとそれに関連するすべてのオブジェクト(TADQuery、TADTransactionなど)は、各時点で単一のスレッドで使用する必要があります」と記載されています。

TCPコマンドを「ゆっくり」送信しても問題ありません。それらを「すばやく」送信すると(AnyDacがまだSQLカーソルを表示しているときに、例外EDatabaseError「フィールドが見つかりません」が発生しますが、もちろん存在します。

TCPコマンドを受信すると、コードはPostMEssageを使用してUM_をメインフォームに送信します。

私が起こっていると思うのは、メインフォームが最初のTCPコマンドの結果としてテーブルからすべての行を読み取っているのに対し、TCPコマンドハンドラーは2番目のコマンドの結果として新しい行を挿入している途中です-したがって "フィールドが見つかりません」を呼び出すとADQuery1.FieldByName()

それは問題のように聞こえますか?

もしそうなら、どうすればそれを防ぐことができますか?クリティカルセクション(どこで、メインスレッド?)を使用できますか?または他の方法はありますか?


[更新]私はちょうど気づきました-これはスレッドの問題ではありえません(私は思います)。TCPコマンドを取得したら、PostMessage()を使用してUM_をメインフォームに送信します。したがって、TCPコマンドがどれだけ速く来ても、私のメインフォームはメッセージキューを介して一度に1つのUM_しか処理できません。TCPコマンドハンドラーは、そのメッセージを送信するための1行であり、d/bアクセスはありません。

しかし、私が理解していないのは、TCPコマンドの間に「しばらく」置いておくとすべて問題ないということですが、「すばやく」送信すると、テーブルのその行にそのようなフィールドがないという例外が発生します。


[更新]実際、私はついにそれを解決しました。問題は、更新がかなり遅いTStringGridを使用することでした。Tehreはそれをより速くする方法ですが、私はそれをTDbGridに変換することにしました。これは非常に迅速に更新されます。

4

2 に答える 2

3

バックグラウンドスレッドで実行されているTCPコマンドハンドラーからAnyDacデータベースを更新し、メインスレッドからAnyDacデータベースから読み取っているようです。AnyDacはマルチスレッドアクセスをサポートしていないと言っているので、これは問題を引き起こします。

1つのオプションは、参加しているすべてのスレッドを整列させて、AnyDacデータベースへのアクセスを待機することです。これは、最初に複数のスレッドを使用するという目標に反します。複数のスレッドを並べて実行するだけの場合、なぜ複数のスレッドを使用するのでしょうか。ミューテックスロック(クリティカルセクションなど)を追加すると、特にアプリの再描画ロジックでそのロックを取得する場合に、デッドロック状態が発生する可能性が高くなります。再描画ロジックにロックは絶対に必要ありません。

別のアプローチは、データベースの更新をメインスレッドに移動することです。これを行うにはまだいくつかのスレッド同期が必要ですが、責任はメインスレッドではなくバックグラウンドスレッドにあります。

データベースの更新をメインスレッドに移動する比較的簡単な方法の1つは、TCPコマンドハンドラーにカスタムUM_メッセージをメインウィンドウに送信させ(既に実行しているように) 、データをメッセージ添付することです。メッセージハンドラは、メッセージからデータを取得し、データベースをデータで更新し、メッセージに添付されたデータを破棄します。

このアプローチでは、AnyDacデータベースオブジェクトへのすべてのアクセスがメインスレッドで行われるため、マルチスレッドの問題は発生しません。再描画サイクルの途中でApplication.ProcessMessagesを呼び出すなどの愚かなことを誰かが行っていない限り、これにより、再描画中に発生するデータベースの更新に関する問題が解決されるはずです。

于 2012-10-11T04:52:58.910 に答える
2

PostMessage()の呼び出しでデータを渡す場合は、メインスレッドでのメッセージの処理中にデータが引き続き有効であることを確認する必要があります。最も簡単な方法は、ハンドラーで次のことを行うことです。

  • 新しいオブジェクトを作成する
  • 新しく作成したオブジェクトに必要なデータをコピーします
  • PostMessageを呼び出して、新しいオブジェクトをパラメータとしてメッセージにメッセージを送信します
 

    procedure ...
    var MsgData: TMsgData;
    begin
      MsgData := TMsgData.Create(ACommand);
      PostMessage(MainFormHWND, WM_MYMSG, Integer(MsgData), 0);  
    end

 

メッセージのハンドラー内

  • メッセージを処理する
  • オブジェクトを解放します
 

    procedure MainForm.HandleMsg(var M: TMessage);
    var MsgData: TMsgData;
    begin
      MsgData := TMsgData(M.WParam);
      // Process command
      MsgData.Free();  
    end;

 
于 2012-10-11T11:41:19.657 に答える