1

要将有关处理状态的信息写入 tcpserver.onexecute(..) 函数内的 GUI,我使用了以下命令序列

ExecuteDUMMYCommand(Global_Send_Record);

BitMap_PaintImageProcess;

TThread.Synchronize(nil, BitMap_PaintImageProcess);

该代码在某些机器上运行良好,但在少数机器上却失败了。代码执行在 TThread.Synchronize 命令处停止。我猜在这些机器上,函数调用被困在死锁中 有机会找出背后的真正问题吗?

程序 BitMap_PaintImageProcess ,在这里我创建了一个 Bitmap 并做了很多绘画的东西,但似乎这段代码从未执行过?


我试图解释很长的代码并简化为要点,关键的线程问题隐藏在处理我的 Bitmapprocessingclass 中的位图中。这个类是在我的 ServerMainForm 的 GUIProcessing 过程中访问的,它也有 INDY TCP Server 组件。

   {---------------   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

关于发布的代码:

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 处理类必须在我的服务器处于活动状态的整个过程中都处于活动状态...

如果您只有一个(全局)实例用于图像处理,那么如果另一个客户端连接,而您仍在处理旧连接(想想 Parallel :))会发生什么?您的图像处理类应该为每个新的连接/上下文单独实例化。对于 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 回答
0

我在我的 BitmapProcessingclass 和现有类的线程安全扩展的想法上添加了更多细节......我需要在其他应用程序中未更改的现有类......我需要在我的应用程序中使用 indy 服务器进行扩展。只有一个客户端连接到一台服务器,或者他必须查询服务器的状态

    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 回答