情况看起来像这样。外部应用程序client.exe每隔约250ms向服务器发送一条命令 MONITOR_ENCODING 。在服务器应用程序中,我使用 IdCmdTCPServer1BeforeCommandHandler 从客户端读取发送的命令。 我将收到的命令( AData 字符串)复制到全局变量 command ,然后将其显示在memo1中。同时,我一直在运行TSupervisorThread,它将全局变量命令复制到本地Copiedcommand变量,然后将新文本分配给命令变量。这是一种不时出现的预期行为,而不是我得到RESET的MONITOR_ENCODING文本吗?
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;
答案 0 :(得分:1)
仅供参考,OnBeforeCommandHandler
事件不是使用TIdCmdTCPServer
读取命令的正确位置。应该应该将每个命令的条目添加到其CommandHandlers
集合中,然后为每个条目分配一个OnCommand
处理程序。 OnBeforeCommandHandler
事件在TIdCmdTCPServer
解析收到的命令之前被触发。出于日志记录目的,这是可以的,只需确保不使用它来驱动处理逻辑即可。将其留给各个OnCommand
事件。
但是,无论哪种方式,命令读取都是在工作线程中完成的。将收到的命令添加到UI时,您与主UI线程不同步。您必须同步。这样做的方法很多,例如TThread.Synchronize()
,TThread.Queue()
,TIdSync
,TIdNotify
,(Send|Post)Message()
等。
更重要的是,您有2个线程(或更多线程,具体取决于同时连接的客户端数量)争用同一个全局变量,而根本不同步对其的访问。您需要锁定变量,例如TCriticalSection
或TMutex
,或使用Indy的TIdThreadSafeString
类。
但是,这不能解决代码所具有的竞争条件。在OnBeforeCommandHandler
为command
分配新值的时间与它读回command
并将其添加到UI的时间之间,您的TSupervisorThread
可以自由修改{ {1}}。那正是您所看到的。在Indy中这不是异常,而是代码中的计时问题。
解决这种竞争状况的最简单方法是将command
而不是AData
添加到您的UI中。这样,command
修改TSupervisorThread
并不重要,您的用户界面将看不到它。
但是,为什么要使用全局command
变量呢?它的真正目的是什么?您是否要让外部线程修改command
解析的命令?您无法控制哪些客户端可以解析实际命令,哪些客户端可以获取假命令。你为什么要这样做?
除了让TIdCmdTCPServer
在主UI线程中执行其99%的工作之外,它对工作线程的使用率很低,您也可以在UI中仅使用TSupervisorThread
。否则,您需要更好地协调线程,例如使用TTimer
对象来发信号通知TEvent
的分配时间和重置时间。
我认为您需要重新考虑您的设计。