TIdCmdTCPServer和与主线程的数据同步[异常?]

时间:2018-12-24 16:54:20

标签: delphi indy10

情况看起来像这样。外部应用程序client.exe每隔约250ms向服务器发送一条命令 MONITOR_ENCODING 。在服务器应用程序中,我使用 IdCmdTCPServer1BeforeCommandHandler 从客户端读取发送的命令。 我将收到的命令( AData 字符串)复制到全局变量 command ,然后将其显示在memo1中。同时,我一直在运行TSupervisorThread,它将全局变量命令复制到本地Copiedcommand变量,然后将新文本分配给命令变量。这是一种不时出现的预期行为,而不是我得到RESET的MONITOR_ENCODING文本吗?

enter image description here

procedure TForm1.IdCmdTCPServer1BeforeCommandHandler(ASender: TIdCmdTCPServer; var AData: String; AContext: TIdContext);
begin

  command:=AData;  //command is a global variable
  form1.Memo1.Lines.Add(IntToStr(form1.Memo1.Lines.Count+1)+'|'+IntToStr(GetTickCount)+'|'+command);

end;

procedure TSupervisorThread.CopyGlobalVariables;
begin

  CopiedCommand:=command; //Copiedcommand declared in TSupervisorThread
  command:='RESET'; 

end;

procedure TSupervisorThread.Execute;
begin

  while Terminated=false do
  begin
    Synchronize(CopyGlobalVariables);
    sleep(250);
  end;

end;

1 个答案:

答案 0 :(得分:1)

仅供参考,OnBeforeCommandHandler事件不是使用TIdCmdTCPServer读取命令的正确位置。应该应该将每个命令的条目添加到其CommandHandlers集合中,然后为每个条目分配一个OnCommand处理程序。 OnBeforeCommandHandler事件在TIdCmdTCPServer解析收到的命令之前被触发。出于日志记录目的,这是可以的,只需确保不使用它来驱动处理逻辑即可。将其留给各个OnCommand事件。

但是,无论哪种方式,命令读取都是在工作线程中完成的。将收到的命令添加到UI时,您与主UI线程不同步。您必须同步。这样做的方法很多,例如TThread.Synchronize()TThread.Queue()TIdSyncTIdNotify(Send|Post)Message()等。

更重要的是,您有2个线程(或更多线程,具体取决于同时连接的客户端数量)争用同一个全局变量,而根本不同步对其的访问。您需要锁定变量,例如TCriticalSectionTMutex,或使用Indy的TIdThreadSafeString类。

但是,这不能解决代码所具有的竞争条件。在OnBeforeCommandHandlercommand分配新值的时间与它读回command并将其添加到UI的时间之间,您的TSupervisorThread可以自由修改{ {1}}。那正是您所看到的。在Indy中这不是异常,而是代码中的计时问题。

解决这种竞争状况的最简单方法是将command而不是AData添加到您的UI中。这样,command修改TSupervisorThread并不重要,您的用户界面将看不到它。

但是,为什么要使用全局command变量呢?它的真正目的是什么?您是否要让外部线程修改command解析的命令?您无法控制哪些客户端可以解析实际命令,哪些客户端可以获取假命令。你为什么要这样做?

除了让TIdCmdTCPServer在主UI线程中执行其99%的工作之外,它对工作线程的使用率很低,您也可以在UI中仅使用TSupervisorThread。否则,您需要更好地协调线程,例如使用TTimer对象来发信号通知TEvent的分配时间和重置时间。

我认为您需要重新考虑您的设计。