1

tcpserver.onexecute(..)関数内のGUIに処理状態に関する情報を書き込むために、次のコマンドシーケンスを使用しました

ExecuteDUMMYCommand(Global_Send_Record);

BitMap_PaintImageProcess;

TThread.Synchronize(nil, BitMap_PaintImageProcess);

コードは一部のマシンでは正常に機能していますが、一部のマシンでは失敗します。コードの実行はatTThread.Synchronizeコマンドで停止します。これらのマシンでは、関数呼び出しがデッドロックの中に閉じ込められていると思います。背後にある本当の問題を理解するチャンスはありますか?

プロシージャBitMap_PaintImageProcess、ここではビットマップを作成し、多くのペイント作業を行いますが、このコードは実行されないようです。


私は非常に長いコードを説明し、要点を減らしてみます。重大なスレッドの問題は、Bitmapprocessingclass内のビットマップの処理に隠されています。このクラスは、INDYTCPサーバーコンポーネントも含むServerMainFormのGUIProcessingプロシージャ内でアクセスされます。

   {---------------   CLASS DEFINITION   -----------------------------}

   TBitMapProcessingClass = class()
        FBitmap : TBitmap;
        FList : TListOfSomething;

        procedure ProcessTheBitmap(....);
        ......   
        (many many functions);
        procedure Init;
        procedure Free;
        Procedure Create;
   end;


   TMainform = class(TForm)

      MyServer : TIdTCPServer;
      aBitMaoProcessingClass : TBitMaoProcessingClass;
      procedure BitMap_PaintImageProcess;
      procedure BitMap_ListProcess;

      .....
      end;       



{-------------------------   Implemantation ------------------------------}


procedure TMainform.IndyTCPServer.Onexecute()
begin

   .......

   ExecuteDUMMYCommand(Global_Send_Record);

   BitMap_PaintImageProcess;

   TThread.Synchronize(nil, BitMap_PaintImageProcess);

   .......


end;



procedure TMainform.BitMap_PaintImageProcess;
   begin

      DoSomeServerVCLStuff(....);

      aBitMapProcessingClass.ProcessTheBitmap;


      DoSomeServerVCLStuff(....);

  end;
4

3 に答える 3

0

BitmapProcessingclassにいくつかの詳細を追加し、既存のクラスのスレッドセーフな拡張機能のアイデアを追加しました...他のアプリで既存のクラスを変更する必要があります...アプリ内にindyサーバーを備えた拡張機能が必要です。1つのクライアントが1つのサーバーに接続するか、サーバーの状態を照会する必要があります

    type TBmpNotify = class(TIdNotify)
   protected
     FBMP: MyImageProcessingClass;

     procedure DoNotify; override;
   public
     constructor Create(aBMP: MyImageProcessingClass);
       function SetImageView(LL, UR: TPoint): Boolean;
              procedure PaintBitMap;
      function InitBitMap(x, y: Integer;
         PixelFormat: TPixelFormat = pf24bit): Boolean;
      destructor free;
   end;


   implementation

   { TBmpNotify }

   constructor TBmpNotify.Create(aBMP: MyImageProcessingClass);
   begin
       //   indise this class  I also create
       //   class.TBitmap
       //   class.TList
       //   much more stuff ....
       FBmp := MyImageProcessingClass.Create;
   end;

   procedure TBmpNotify.DoNotify;
   begin
     inherited;

   end;

   destructor TBmpNotify.free;
   begin

       FBmp.Free;

       inherited;
   end;

   function TBmpNotify.InitBitMap(x, y: Integer;
     PixelFormat: TPixelFormat): Boolean;
   begin
       //   Write values to the List
       //   also modify TBitmap
       //   execution time of this function ~ 5 min
       FBmp.InitBitMap(x,y,PixelFormat)
   end;

   procedure TBmpNotify.PaintBitMap;
   begin
       //   much TBitmap, bitmap.canvas .... is used
       //   execution time of this function ~ 1  min
      FBmp.PaintBitMap;
   end;

   function TBmpNotify.SetImageView(LL, UR: TPoint): Boolean;
   begin
      //  this function takes about 1 min
      FBmp.SetImageView(LL, UR);
   end;

   end.
于 2012-10-28T10:19:17.860 に答える
0

投稿されたコードについて:

procedure TMainform.IndyTCPServer.Onexecute()
begin
   .......
   ExecuteDUMMYCommand(Global_Send_Record);
   BitMap_PaintImageProcess; //-> You do VCL stuff in the context of Indy's thread!
   TThread.Synchronize(nil, BitMap_PaintImageProcess);
end;

あなたがBitMap_PaintImageProcess()呼び出すでDoSomeServerVCLStuff(....)。OnExecute は、現在のコンテキストの Indy のスレッドから起動されることを忘れないでください。つまり、スレッド セーフではない別のスレッド (メイン スレッド以外) から VCL を変更します。

あなたのコメントについて:

...しかし、ここでは、複雑な TBitmap 処理クラスは、サーバーがアクティブな間ずっと生きている必要があります...

画像処理用の (グローバル) インスタンスが 1 つしかない場合、古い接続をまだ処理している間に別のクライアントが接続するとどうなりますか (並列を考えてください:))? 画像処理クラスは、新しい接続/コンテキストごとに個別にインスタンス化する必要があります。GUI の更新には、TIdNotify の子孫を使用できます。

考えられる解決策:

type 
{ tIdNotify Stuff }
TVclProc= procedure(imgClass: tMyImageProcessingClass) of object;

tIdNotifyDescendant = (tIdNotify)
  protected
    fImgClass: tMyImageProcessingClass;
    fProc: TVclProc;
    procedure DoNotify; override;
  public
    class procedure updateVcl(imgClass: tMyImageProcessingClass; vclProc: TVclProc);
end;

procedure tIdNotifyDescendant.DoNotify;
begin
 inherited DoNotify;
 FProc(fImgClass);
end;

class procedure tIdNotifyDescendant.updateVcl(imgClass: tMyImageProcessingClass; vclProc: TVclProc);
begin
 with Create do
 begin
   fImgClass := imgClass;
   fProc := vclProc;
   Notify;
 end;
end;

{ Indy stuff & other logic }

procedure TForm1.IdTCPServer1Connect(AContext: TIdContext);
begin
  //  Create your instance when the client connects
  AContext.Data := tMyImageProcessingClass.Create;
end;

procedure TForm1.IdTCPServer1Disconnect(AContext: TIdContext);
begin
  //  Do cleanup
  if assinged(AContext.Data) then
    (AContext.Data as tMyImageProcessingClass).Free // Casting just for clarity
end;

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
  imgProcClass: tMyImageProcessingClass;
begin
  imgProcClass := acontext.Data as tMyImageProcessingClass;
  //  Do image processing
  //  Notify GUI for the progress:
  tIdNotifyDescendant.updateVcl(AContext.data as tMyImageProcessingClass);
end;

ヒント: JPEG 処理を行い、Draw() メソッドを使用する場合は、次の点に注意してください: TJPEGImage.Draw() はスレッド セーフではありません

于 2012-10-27T10:30:25.413 に答える
0

BitMap_PaintImageProcess() が実際に何をするかわからないので、いくつかの推測があります。

  • TThread.Synchronize 呼び出しで、socket/idContext からデータを読み込もうとしましたが、データはまだ利用できません。これにより、データが利用可能になるまでメイン スレッドがブロックされます。しかし、基盤となるソケット バッファからの読み取りを担当する Indy のスレッドは、現在、OnExecute イベントの TThread.Synchronize 呼び出しによってブロックされています。つまり、デッドロックが発生しています。
  • TThread.Synchronize 呼び出しでは、Application.ProcessMessages を使用します (よくある間違い)。
  • OnExecute イベントでは、クリティカル セクションを入力します。次に、TThread.Synchronize の呼び出し中に、同じクリティカル セクションに入ろうとします。
  • onExecute イベントで GUI を変更します。onExecute はスレッド セーフではないため、Indy のスレッドから VCL/FM にアクセスすると、ランダムなデッドロック (見つけにくい) など、予測できない結果が生じる可能性があります。

MadExcept / Eurekalog を使用することをお勧めします。メインスレッドが「フリーズ」しているかどうかを確認するオプションがあります。それが発生すると(デッドロック中に)、現在のコールスタックが表示されます。コール スタックがあれば、デッドロックの原因となっている関数を特定できます。

于 2012-10-26T22:49:18.073 に答える