来自流的函数读取行是阻塞的

时间:2012-08-30 18:45:48

标签: c# loops stream stack-trace execution

我写了以下C#函数:

static string ReadLineCRLF(System.IO.Stream Stream, ref byte[] sharedBuffer, int bufferSize = 1024)
{
    StringBuilder responseString = new StringBuilder("");
    string returnString = "";
    byte[] buffer = new byte[bufferSize];
    bool stopreading = false;
    bool firstexecution = true;
    while (!stopreading)
    {
        int readBytes;
        if (firstexecution && sharedBuffer.Length > 0)
        {
            readBytes = sharedBuffer.Length;
            sharedBuffer.CopyTo(buffer, 0);
        }
        else
        {
            readBytes = Stream.Read(buffer, 0, bufferSize); //BLOCKING HERE
        }
        firstexecution = false;
        if (readBytes > 0)
        {
            int crIndex = Array.IndexOf(buffer, (byte)13); //13 = ASCII value for a carriage return
            if (crIndex > -1 && Array.IndexOf(buffer, (byte)10, crIndex + 1) == crIndex + 1) //10 = ASCII value for line feed
            {

                stopreading = true;
                sharedBuffer = readBytes - crIndex - 2  > 0 ? ArraySlice<byte>(buffer, crIndex+2, readBytes-crIndex-2) : new byte[] { };
                readBytes = crIndex;
            }
            if (readBytes > 0)
            {
                responseString.Append(System.Text.Encoding.ASCII.GetString(buffer, 0, readBytes));
            }
            if (stopreading)
            {
                returnString = responseString.ToString();
            }
        }
        if (!stopreading && readBytes <= 0)
        {
            returnString = null;
            stopreading = true;
            sharedBuffer = new byte[] { };
        }
    }
    return returnString;

}

根据我的Stack Explorer,这个函数在readBytes = Stream.Read(buffer, 0, bufferSize);阻塞并且耗尽了很多计算机性能。这个函数应该做的唯一事情是从流中读取一行,该行仅以CRLF(“\ r \ n”)结束。

根据MSDN Stream.Read返回less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.,它通常应该阻止而不会耗尽CPU性能。 The implementation will block until at least one byte of data can be read, in the event that no data is available. Read returns 0 only when there is no more data in the stream and no more is expected (such as a closed socket or end of file)

那么为什么呢 - 根据我的CLR Stack Explorer - 消耗了如此多的性能(高达70%)?我没有看到任何逻辑错误,我认为它应该等到可以接收到一些字节。似乎这种行为并不总是在Windows服务器上执行应用程序开始后的一两天内发生。

其他解释: 当使用块读取字节时,可能会读取太多字节并将其存储在缓冲区中。因此我使用共享缓冲区,允许它再次使用它继续阅读下一行。一旦完整读取一行,我就将其从缓冲区中删除。

ArraySlice函数如下所示:

public static T[] ArraySlice<T>(T[] data, int index, int length)
{
    T[] result = new T[length];
    Array.Copy(data, index, result, 0, length);
    return result;
}

1 个答案:

答案 0 :(得分:1)

您如何知道Read正在阻止,而不只是花一点时间去做它需要的东西?

IO很贵;我希望完全可以预期此方法花费的大量时间都在Read调用之内。 70%你说的听起来是正确的。您似乎没有滥用该方法,因此读取所花费的时间百分比越高,实际上意味着执行其他所有操作的开销越低。我没有把它当成70%的时间去读书,而是将其视为失去30%的时间从事非阅读活动。这不是一个糟糕的统计数据,但也不是没有改进的余地。

在深入研究之前,请确保您不在微观优化领域。我现在可以告诉你,看起来你看起来并没有做错,所以除非你对你的代码进行基准测试并确定它的运行速度比你的要求可接受的慢,所以不要担心性能。如果你的程序执行速度不够快,那么你需要从目前的工作时间以及需要多长时间才能“足够快”开始。