我的代码中某处的无限循环
Posted
技术标签:
【中文标题】我的代码中某处的无限循环【英文标题】:An infinite loop somewhere in my code 【发布时间】:2011-08-31 05:06:45 【问题描述】:我有这个 Java 游戏服务器,它最多可以处理 3,000 个 tcp 连接,每个玩家,或者每个 tcp 连接都有自己的线程,每个线程都是这样的:
public void run()
try
String packet = "";
char charCur[] = new char[1];
while(_in.read(charCur, 0, 1)!=-1 && MainServer.isRunning)
if (charCur[0] != '\u0000' && charCur[0] != '\n' && charCur[0] != '\r')
packet += charCur[0];
else if(!packet.isEmpty())
parsePlayerPacket(packet);
packet = "";
kickPlayer();
catch(IOException e)
kickPlayer();
catch(Exception e)
kickPlayer();
finally
try
kickPlayer();
catch(Exception e);
MainServer.removeIP(ip);
代码运行良好,我知道每个玩家的每个线程都是一个坏主意,但我现在也只能这样。服务器在快速机器(6cor x2、64 位、24GB RAM、Windows Server 2003)上运行良好。
但是在某个时刻,大约 12 小时的正常运行时间后,服务器开始在某个地方循环......我知道这是因为 java 进程无限地占用 99% 的 CPU 直到下一次重新启动。 而且我很难分析应用程序,因为我不想打扰玩家。我使用的分析器(visualvm)总是在不告诉我问题出在哪里的情况下对服务器进行追逐。
无论如何,在上面的那段代码中,我认为问题可能来自于此:
while(_in.read(charCur, 0, 1)!=-1)
(_in
是客户端套接字的BufferedReader
)。
_in.read()
是否有可能无限返回其他内容以保持我的代码运行并占用 99% 的资源?我的代码有问题吗?没看懂,只写了一半。
【问题讨论】:
任何具有 try...catch 结构的东西都不能正常运行。 如果您像这样忽略所有异常,您将永远无法找出问题所在。正确处理异常。 我只是想 100% 确定 kickPlayer() 将被执行。 你听说过 finally 块吗? 至少 在 catch 中执行e.printStackTrace()
,否则您将无法提示抛出什么异常。并且 finally 块将始终被调用,因此在出现异常时 kickPlayer
将被调用两次。顺便说一句,您确定不是 kickPlayer
循环或负责它吗?
【参考方案1】:
一次读取一个字符几乎与使用 += 构建字符串一样慢。我无法告诉你哪个更糟。如果单个连接使用这种方法连接整个核心,我不会感到惊讶。
最简单的“修复”是使用 BufferedReader 和 StringBuilder。
然而,读取数据最有效的方法是将字节读取到 ByteBuffer 中并解析“行”。我假设您正在接收 ASCII 文本。您可以编写解析器,使其能够在一个阶段处理内容和行尾(即一次数据传递)
使用最后一种方法,这里有一个示例(包括代码),说明我从套接字解析 XML 消息并以 XML 回复。典型延迟为 16 微秒,吞吐量为每秒 264K。
http://vanillajava.blogspot.com/2011/07/send-xml-over-socket-fast.html
您可以执行以下可能足够快的操作
BufferedReader br = new BufferedReader(_in);
for(String line; ((line = br.readline()) != null;)
if(line.indexOf('\0') >= 0)
for(String part: line.split("\0"))
parsePlayerPacket(part);
else
parsePlayerPacket(line);
如果您发现此解决方案非常简单并且您熟悉 ByteBuffer,则可以考虑使用这些解决方案。
【讨论】:
我不知道你在说什么,但我有一种感觉你会解决这个问题..你能给我一个你的意思的代码示例(简单的)吗?我可以在我的代码中包含一些东西只是为了得到一个想法?谢谢 @Reacan,在这种情况下,最好坚持使用我包含的 BufferedReader 的更简单的解决方案。【参考方案2】:我在我写的一个应用程序中遇到了同样的问题。我的应用程序占用了 50% 的 cpu(在双核中)。
我为解决问题所做的就是让线程休眠 1 个时间点
Thread.sleep(1);
希望对你有帮助
编辑:
哦,那是为了什么?catch(IOException e)
kickPlayer();
catch(Exception e)
kickPlayer();
我认为你不需要 IOException Catch(Exception catch,捕获各种异常)
【讨论】:
恐怕它会减慢速度。对于可能非常长的数据包中的每个字节的睡眠(1)可能不是一个好主意。不过我会试一试,谢谢。【参考方案3】:那个异常处理简直伤了我的眼睛。在 catch 块中调用 kickPlayer() 是没有意义的,因为你最终会再次调用它。最终(几乎)总是执行。
现在关于你的问题,忘记我之前的答案,我有点睡着了XD。在发布的 while 循环中,我看不到任何容易永远循环的东西。 InputStream.read() 在没有更多数据时返回 -1 或引发异常。问题一定出在其他代码中,或者可能是线程问题。
正如他们在其他答案中告诉您的那样,尝试使用缓冲流,一次读取一个字符块而不是一个字符,并替换 StringBuilder 的 append 方法的字符串连接。这应该会提高性能,但不确定是否能解决问题(可能出现在 24 小时而不是 12 小时)。
【讨论】:
以上是关于我的代码中某处的无限循环的主要内容,如果未能解决你的问题,请参考以下文章