在TCPServer的Indy10死锁

时间:2012-10-26 20:50:33

标签: delphi indy

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

ExecuteDUMMYCommand(Global_Send_Record);

BitMap_PaintImageProcess;

TThread.Synchronize(nil, BitMap_PaintImageProcess);

代码在某些机器上运行良好,但在少数机器上运行失败。代码执行在atThhread.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;

3 个答案:

答案 0 :(得分: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。他们都有选项来检查主线程是否“冻结”。当发生这种情况时(在死锁期间),它们将显示当前的调用堆栈。拥有调用堆栈,您可以找出导致死锁的功能。

答案 1 :(得分: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(....)。不要忘记Onyecute是从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() is not thread safe

答案 2 :(得分: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.