首页 > 解决方案 > Delphi XE4 + Indy TCP服务器:大量线程

问题描述

Windows 服务有多个永久 TCP 连接,接受数据,在单独的线程中处理它,定期向所有连接的客户端发送更新。处理必须尽可能实时,客户端每 2-4 秒发送一条消息。

当使用 1900 个连接时,该服务有大约 1970 个线程。到现在还好。但是当线程总数超过 2000-2200 时,事情就变得奇怪了——有时处理线程会无缘无故地阻塞 10 秒,即使使用实时优先级也是如此。在 10 秒内,传入队列会填满数千个请求,因此即使是一秒钟的延迟也是不可接受的。尝试使用 TSchedulerOfThread 来限制线程数,但实际上这只限制了连接数......

我知道 Indy 每个 TCP 连接消耗一个线程。所以我测试了使用 Windows 消息传递的 Overbyte ICS(不完全是这个的粉丝,但没有真正的选择)。线程数显着降低(仍然是 70,但没关系),在 3000 个连接时也能正常工作。几个小时后,服务在没有调用堆栈的情况下随机崩溃(正在使用 madExcept)。Windows 事件日志中只有一条记录标记为“应用程序崩溃”。

所以 Indy 和 ICS 都不适合我。

中间有什么解决办法吗?我可以负担得起使用 1000 个线程,但不是 3000...

标签: multithreadingdelphitcp

解决方案


根据评论中的附加信息,问题很可能来自Sleep()您的工作线程,通过以下文档Sleep()

休眠间隔过后,线程就可以运行了。如果您指定 0 毫秒,线程将放弃其时间片的剩余部分但保持就绪状态。请注意,不保证就绪线程会立即运行。

我认为 Windows 中的线程调度程序并不能很好地处理这么多线程,而且很可能在你之后Sleep(5),Windows 仍然忙于在其他线程之间切换。此外,如果您每秒收到 1000 条消息,那么即使是 5 毫秒的睡眠时间也是很长的。

在这种情况下,我们总是使用TThreadedQueue(不确定它是否在您的 Delphi 版本中可用)和PushItem()/PopItem()用于生产者/消费者的数据传输。你不需要让你的线程进入睡眠状态,因为“PopItem() waits for a new message to arrive in the queue (it uses internal logic with aTMonitor`同步)。我们的工作线程看起来像这样:

while not Terminated do
begin
  FMsg := MsgQueue.PopItem;  //ThreadQueue waits (for PopTimeout) here for item to appears
  if Assigned(FMsg) then
  begin
    AddLogDebug('Begin DoProcessMsg');
    DoProcessMsg;
    AddLogDebug('End DoProcessMsg');
  end;
end;

推荐阅读