另一种 TIdTCPServer 死锁情况

发布于 2024-11-28 11:24:34 字数 1148 浏览 0 评论 0原文

我遇到了已知问题:IdTCPServer 在尝试停用它时挂起。我读过很多有关此类问题的文章,并且我知道发生了僵局。但我很难理解到底是哪个代码导致了问题。我的应用程序甚至没有窗口,因此对 VCL 组件的同步调用不是原因。有 2 个事件处理程序 OnConnect 和 OnExecute。该问题出现在 OnConnect 中。代码:

TMyServer = class
...
private
FServer: TIdTCPServer;
...
end;

TMyServer.ServerConnect(AContext...);  //onconnect
begin
  try
    if not Condition then
    begin
      ...
      AContext.Connection.Disconnect;
      Stop;
    end
    else Start;
  except
    on E: Exception do
      if E is EIdException then raise;
      ...  
  end;

TMyServer.Start;
begin
  ...
  FServer.active := true;
  FServer.StartListening;
  ...
end;

TMyServer.Stop;
begin
  ...
  FServer.StopListening;
  FServer.Active := false;
  ...
end;

TMyServer.ServerExecute(AContext...);  //onexecute
begin
  LLine := AContext.Connection.IOHandler.ReadLn;
  case (LLine) of
  ...
  end;
end;

ServerExecute的代码相当庞大,所以我不会在这里发布它。此外,我认为问题不在其中。
当客户端连接到服务器且条件为 false 时,服务器会尝试断开此客户端的连接并挂在线路上 FServer.Active := false;
我已经从代码中排除了所有日志记录(我认为问题出在线程对日志文件的访问中)。因此,在事件处理程序中,只有计算,没有任何可能导致死锁的内容)。我读过有关重新引发印地例外的信息,但这对我也没有帮助。
我将不胜感激任何帮助和解释,因为我认为我不完全理解这种情况的目的。

I'm having the known issue: IdTCPServer hangs while trying to deactivate it. I've read lots of articles concerning such problem and I'm aware of the deadlock happening. But it's difficult for me to understand which code exactly causes the problem. My app doesn't have even a window so the synchronized call to VCL component isn't the reason. Got 2 event handlers OnConnect and OnExecute. The problem appears in OnConnect. The code:

TMyServer = class
...
private
FServer: TIdTCPServer;
...
end;

TMyServer.ServerConnect(AContext...);  //onconnect
begin
  try
    if not Condition then
    begin
      ...
      AContext.Connection.Disconnect;
      Stop;
    end
    else Start;
  except
    on E: Exception do
      if E is EIdException then raise;
      ...  
  end;

TMyServer.Start;
begin
  ...
  FServer.active := true;
  FServer.StartListening;
  ...
end;

TMyServer.Stop;
begin
  ...
  FServer.StopListening;
  FServer.Active := false;
  ...
end;

TMyServer.ServerExecute(AContext...);  //onexecute
begin
  LLine := AContext.Connection.IOHandler.ReadLn;
  case (LLine) of
  ...
  end;
end;

The code of ServerExecute is quite huge so I won't post it here. Besides I suppose the problem isn't in it.
When the client connects to the server and Condition is false the server tries to disconnect this client and hangs on the line FServer.Active := false;
I've already excluded all the logging from the code (I thought the problem was in the threads access to log file). So in the event handlers there are only calculations and nothing which can cause the deadlock). I've read about re-raising of Indy exceptions but that didn't help me too.
I would appreciate any help and explanation cause I think I don't fully understand the purpose of this situation.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

念三年u 2024-12-05 11:24:35

您正尝试从服务器的事件处理程序之一内部停用服务器。这就是你的代码陷入僵局的原因。

Active 属性设置为 False 将终止所有正在运行的客户端线程并等待它们完全终止。 OnConnectOnDisconnectOnExecute 事件在这些线程的上下文中触发。因此,您最终会得到一个试图等待自身并出现死锁的线程。

要让服务器安全地自行停用,您必须异步执行此操作,例如使用 TIdNotify 类,例如:

uses
  ..., IdSync;

TMyServer.ServerConnect(AContext...);  //onconnect
begin
  try
    if not Condition then
    begin
      ...
      AContext.Connection.Disconnect;
      TIdNotify.NotifyMethod(Stop);
    end
    else
      TIdNotify.NotifyMethod(Start);
  except
    on E: Exception do
      if E is EIdException then raise;
      ...  
  end;
end;

顺便说一句,您不需要调用 StartListening()StopListening() 手动方法。 Active 属性设置器在内部为您完成此操作。

You are trying to deactivate the server from inside one of its event handlers. That is why your code is deadlocking.

Setting the Active property to False terminates all running client threads and waits for them to fully terminate. The OnConnect, OnDisconnect, and OnExecute events are triggered in the context of those threads. So you end up with a thread that is trying to wait on itself and deadlocks.

To have the server deactivate itself safely, you must do it asynchronously, such as by using the TIdNotify class, for example:

uses
  ..., IdSync;

TMyServer.ServerConnect(AContext...);  //onconnect
begin
  try
    if not Condition then
    begin
      ...
      AContext.Connection.Disconnect;
      TIdNotify.NotifyMethod(Stop);
    end
    else
      TIdNotify.NotifyMethod(Start);
  except
    on E: Exception do
      if E is EIdException then raise;
      ...  
  end;
end;

BTW, you do not need to call the StartListening() and StopListening() methods manually. The Active property setter does that internally for you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文