【问题标题】:Reducing CPU overhead while reading from Sockets using JAVA使用 JAVA 从 Socket 读取时减少 CPU 开销
【发布时间】:2013-04-12 07:56:32
【问题描述】:

我有两个线程会增加 CPU 开销。 1. 以同步方式从套接字读取。 2. 等待接受来自其他客户端的连接

问题 1,我只是试图读取来自客户端的任何数据,但我不能使用 readline,因为传入的数据有我标记的换行符以了解消息的标题结尾。所以我在线程中使用这种方式,但它增加了 CPU 开销

 public static String convertStreamToString(TCPServerConnectionListner socket) throws UnsupportedEncodingException, IOException, InterruptedException {

        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getSocket().getInputStream()));
        // At this point it is too early to read. So it most likely return false
        System.out.println("Buffer Reader ready? " + reader.ready());
        // StringBuilder to hold the response
        StringBuilder sb = new StringBuilder();
        // Indicator to show if we have started to receive data or not
        boolean dataStreamStarted = false;
        // How many times we went to sleep waiting for data
        int sleepCounter = 0;
        // How many times (max) we will sleep before bailing out
        int sleepMaxCounter = 5;
        // Sleep max counter after data started
        int sleepMaxDataCounter = 50;
        // How long to sleep for each cycle
        int sleepTime = 5;
        // Start time
        long startTime = System.currentTimeMillis();
        // This is a tight loop. Not sure what it will do to CPU
        while (true) {
            if (reader.ready()) {
                sb.append((char) reader.read());
                // Once started we do not expect server to stop in the middle and restart
                dataStreamStarted = true;
            } else {
                Thread.sleep(sleepTime);
                if (dataStreamStarted && (sleepCounter >= sleepMaxDataCounter)) {
                    System.out.println("Reached max sleep time of " + (sleepMaxDataCounter * sleepTime) + " ms after data started");
                    break;
                } else {
                    if (sleepCounter >= sleepMaxCounter) {
                        System.out.println("Reached max sleep time of " + (sleepMaxCounter * sleepTime) + " ms. Bailing out");
                        // Reached max timeout waiting for data. Bail..
                        break;
                    }
                }
                sleepCounter++;
            }

        }
        long endTime = System.currentTimeMillis();

        System.out.println(sb.toString());
        System.out.println("Time " + (endTime - startTime));

        return sb.toString();
    }

问题 2,我不知道这样做的最佳方法是什么,我只是有一个线程不断等待其他客户并接受它。但这也需要大量的 CPU 开销。

 // Listner to accept any client connection
    @Override
    public void run() {

        while (true) {
            try {
                mutex.acquire();
                if (!welcomeSocket.isClosed()) {
                    connectionSocket = welcomeSocket.accept();
                   // Thread.sleep(5);
                }


            } catch (IOException ex) {
                Logger.getLogger(TCPServerConnectionListner.class.getName()).log(Level.SEVERE, null, ex);
            } catch (InterruptedException ex) {
                Logger.getLogger(TCPServerConnectionListner.class.getName()).log(Level.SEVERE, null, ex);
            }
            finally
            {
                mutex.release();
            }

        }
    }
}

Profiler 图片也会有所帮助,但我想知道为什么 SwingWorker 线程要花那么多时间?

更新问题一的代码:

    public static String convertStreamToString(TCPServerConnectionListner socket) throws UnsupportedEncodingException, IOException, InterruptedException {

            byte[] resultBuff = new byte[0];
            byte[] buff = new byte[65534];
            int k = -1;
            k = socket.getSocket().getInputStream().read(buff, 0, buff.length);
                byte[] tbuff = new byte[resultBuff.length + k]; // temp buffer size = bytes already read + bytes last read
                System.arraycopy(resultBuff, 0, tbuff, 0, resultBuff.length); // copy previous bytes
                System.arraycopy(buff, 0, tbuff, resultBuff.length, k);  // copy current lot
                resultBuff = tbuff; // call the temp buffer as your result buff

        return new String(resultBuff);
    }

}
        ![snapshot][2]

【问题讨论】:

  • 嗯,我不明白问题 2。互斥锁是做什么用的?此外,由于 accept() 是一个阻塞调用,因此 CPU 使用应该是最小的。
  • welcomeSocket 可以从主线程访问,所以我在保护它。好吧好吧,那么它占用的CPU周期不是最多的,那么第一个问题呢? read() 也阻塞了,为什么它会占用 CPU 周期?
  • 好的,现在你有一个阻塞读取,但它需要 60 毫秒。首先,您的总 CPU 使用率下降了吗?我问的是你的其他线程中是否有更多可避免的循环。
  • @MartinJames 我将提供 netbeans 分析器的快照。它做了一点点,只是减去了 3%。

标签: java multithreading sockets


【解决方案1】:

只需摆脱ready() 调用和阻塞。您在ready() 为假时所做的一切实际上是完全浪费时间,包括睡眠。 read() 将阻塞适当的时间。 sleep() 不会。您要么睡眠时间不够长,这会浪费 CPU 时间,要么睡眠时间过长,这会增加延迟。偶尔你可能会睡到正确的时间,但这是 100% 的运气,而不是好的管理。如果您想要读取超时,请使用读取超时。

【讨论】:

  • @EJP 我已经更新了帖子中的代码。 read() 花了大约 60 毫秒。这实在是太多了。
  • @AhmedSaleh 所以?这就是数据到达所需的时间。无论如何摆弄你的代码都无法改变它。
【解决方案2】:

您似乎在等待一段时间后没有更多数据。

我建议你使用Socket.setSoTimeout(timeout in seconds)

一个更好的解决方案是不需要这样做,通过一个协议让您知道何时到达数据末尾。仅当您的服务器实施不佳并且您无法修复它时,您才会这样做。

【讨论】:

  • 所以我最好把while(true)去掉,直接读,然后设置socket.SetSoTimeout()?它会影响 read() 函数吗?
  • Profilers 可以报告 read() 和阻塞,但这并不意味着它正在使用 CPU。
  • 这正是它的作用。顺便说一句,如果可以避免,我不会使用 StringBuilder,最好在获得数据时处理它。
  • 你认为使用 Java.NIO 会有帮助吗?我想使用 read() 来读取我得到的数据,但它会阻塞线程。
  • 您可以使用非阻塞 IO,但要复杂得多,但我不清楚您获得了什么。即为什么在这种情况下阻塞线程是一个问题?
【解决方案3】:

对于问题 1。100% CPU 可能是因为您正在从 BufferedReader.read() 读取单个字符。相反,您可以将数据块读取到数组中并将其添加到您的字符串构建器中。

【讨论】:

    猜你喜欢
    • 2017-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-05
    • 2010-11-21
    • 2014-04-10
    • 1970-01-01
    相关资源
    最近更新 更多