【问题标题】:Reading on a NetworkStream = 100% CPU usage读取 NetworkStream = 100% CPU 使用率
【发布时间】:2011-04-24 01:08:56
【问题描述】:

我正在从处于 while 循环中的 NetworkStream 读取数据。问题是我看到 100% 的 CPU 使用率。有没有办法阻止这种情况发生?

这是我目前所拥有的:

    while (client != null && client.Connected)
            {

                NetworkStream stream = client.GetStream();
                data = null;

                try
                {
                    // Check if we are still connected.
                    if (client.Client.Poll(0, SelectMode.SelectRead))
                    {
                        byte[] checkConn = new byte[1];

                        if (client.Client.Receive(checkConn, SocketFlags.Peek) == 0)
                        {
                            throw new IOException();
                        }
                    }

                    if (stream.DataAvailable)
                    {
                        //Read the first command
                        WriteToConsole("Waiting for next command");
                        data = ReadStringFromClient(client, stream);
                        WriteToConsole("Received Command: " + data);
                    }
                }

...代码继续...

ReadStringFromClient 代码:

   private string ReadStringFromClient(TcpClient clientATF, NetworkStream currentStream)
    {
        int i;
        string builtString;
        byte[] stringFromClient = new byte[256];

        if (clientATF.Connected && currentStream.CanRead)
        {

            i = currentStream.Read(stringFromClient, 0, stringFromClient.Length);
            builtString = System.Text.Encoding.ASCII.GetString(stringFromClient, 0, i);

        }

        else
        {
            return "Connection Error";
        }

        return builtString;

    }

【问题讨论】:

    标签: c# .net sockets cpu-usage networkstream


    【解决方案1】:

    您看到 100% 的 CPU 使用率的原因是您总是在做某事,这是您无限循环且没有延迟的结果。

    基本语义是:

    1. 检查客户端是否连接
    2. 轮询客户
    3. 检查数据是否可用
    4. 读取数据
    5. 返回步骤 1

    既然你在这个循环中,你总是在做某事,不管它是什么。最简单的语义是在循环结束时执行 Thread.sleep() 。这将帮助您,而无需进行许多更改。但是,无论睡眠时间是多少,您都会引入延迟,这并不是真正的正确方法(但可能适合您的情况)。

    如果您想要一个高性能服务器,或者在连接 1 个或多个套接字时使用合适的低 CPU 的东西,正确的方法是了解异步套接字。做一个快速的谷歌搜索可能是最好的学习方法,我不记得有任何特别好的文章。异步 IO 的好处基本上就是你只有在有事的时候才会使用 CPU 时间。收到数据后,将调用您的方法来执行您必须执行的任何处理。

    【讨论】:

    • 这是最简单的解决方案,但是……我现在知道异步套接字应该是我的起点。
    • 我过去曾使用带有套接字的轮询循环,发现循环结束时非常短暂的阻塞延迟可以解决这个问题。以下代码运行良好:System.Threading.Thread.Sleep(10);
    • @Nate 这基本上是我的建议,但请深入阅读,尽管它确实有效并且可能很容易做到,但这是一个非常糟糕的设计。
    • 但是 Socket.Receive 在收到数据之前不会阻塞吗?这会在阻塞时消耗CPU吗? (见msdn.microsoft.com/en-us/library/8s4y8aff(v=vs.110).aspx
    【解决方案2】:

    这是因为您不断要求 CPU 为您做一些事情 - 即检查流上是否有任何可用的内容。

    这是一种非常常见的模式,您需要学会适当地处理它。它被称为异步 IO,好消息是 .NET 框架具有广泛的支持,使您可以轻松地完成您想要的工作,而无需持续将 CPU 运行到 100%。

    最大的问题是您的代码在无限循环中运行,不断调用client.Client.Poll(0, SelectMode.SelectRead),它会立即返回答案。相反,您应该要求框架在发生有趣的事情时通知您,例如当有数据可从网络流中读取时。您可以通过多种方式做到这一点。一种方法是使用 NetowrkStream 的BeginRead 方法。

    这是一个用于客户端应用程序的 asynchronous sockets programming example 示例。

    【讨论】:

    • 我已经开始研究这个了。谢谢
    【解决方案3】:

    您的代码包含很多...噪音。你不需要它。

    100% CPU 负载的原因是您在等待数据可用。你不需要这样做。 Read 将阻塞直到数据可用。您也不需要为要接收的每个数据块重新创建 NetworkStream

    如果您使用StreamReader,您的代码可以大大简化:

    using (var reader = new StreamReader(new NetworkStream(socket))
    {
        char[] buffer = new char[512];
        int received;
        while ((received = reader.Read(buffer, 0, buffer.Length)) > 0)
        {
            string s = new string(buffer, 0, received);
            Console.WriteLine(s);
        }
    }
    

    Read 阻塞直到数据可用。连接处于活动状态时代码循环。如果您使用 ReadLine 而不是读入 char 缓冲区,则可以进一步简化代码。

    如果您不想在数据可用之前阻塞线程,请查看异步读取。

    【讨论】:

    • 这不是一个坏建议,只是要注意,只有当您的流是文本流时才应该这样做。如果你已经实现了一个协议(你应该这样做),那么你需要自己做一些工作:-)
    猜你喜欢
    • 2017-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-17
    • 1970-01-01
    相关资源
    最近更新 更多