我的代码中某处的无限循环

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 小时)。

【讨论】:

以上是关于我的代码中某处的无限循环的主要内容,如果未能解决你的问题,请参考以下文章

如果数字大于或等于772000000000000,为什么我的python代码会产生无限循环?

无限循环(动作脚本3)

无限循环(动作脚本 3)

动画图像时无限循环

Roblox 无限旋转循环

如何在我修改的BFS程序中解决无限循环?