C#并发文件流读/写eof

时间:2014-07-08 08:42:12

标签: c# .net multithreading filestream streamreader

我有一个线程在日志文件中生成行:

var t1 = Task.Factory.StartNew(() =>
{
    using (var fileStream = File.Open(file, FileMode.Create, FileAccess.Write, FileShare.Read))
    using (var streamWriter = new StreamWriter(fileStream))
    {
        for (var i = 0; i < 10; i++)
        {
            streamWriter.WriteLine(i);
            streamWriter.Flush();
            Thread.Sleep(1000);
        }
    }

    File.Delete(file);
});

我有一个线程从同一个日志文件中读取行:

// Reads lines from the log file.
var t2 = Task.Factory.StartNew(() =>
{
    Thread.Sleep(500); // Horrible wait to ensure file existence in this test case.
    using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read | FileShare.Write))
    using (var streamReader = new StreamReader(fileStream))
    {
        string line;
        while ((line = streamReader.ReadLine()) != null) Console.WriteLine(line);
        // FIXME: The stream reader stops, instead of doing a continous read.
        Console.WriteLine("End of file");
    }
});

读者应该读取所​​有写入的行,因此它应该等待更多数据,而不是在第一次遇到EOF时停止。我不介意读者永远不会“完成”,只要文件继续存在,读者就可以继续阅读。我怎样才能做到这一点?用于复制目的的完整代码:

using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

namespace PlayGround
{
    internal static class Program
    {
        private static void Main()
        {
            const string file = "test.log";

            // Writes lines to the log file.
            var t1 = Task.Factory.StartNew(() =>
            {
                using (var fileStream = File.Open(file, FileMode.Create, FileAccess.Write, FileShare.Read))
                using (var streamWriter = new StreamWriter(fileStream))
                {
                    for (var i = 0; i < 10; i++)
                    {
                        streamWriter.WriteLine(i);
                        streamWriter.Flush();
                        Thread.Sleep(1000);
                    }
                }

                File.Delete(file);
            });

            // Reads lines from the log file.
            var t2 = Task.Factory.StartNew(() =>
            {
                Thread.Sleep(500); // Horrible wait to ensure file existence in this test case.
                using (
                    var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read,
                        FileShare.Delete | FileShare.Read | FileShare.Write))
                using (var streamReader = new StreamReader(fileStream))
                {
                    string line;
                    while ((line = streamReader.ReadLine()) != null) Console.WriteLine(line);
                    // FIXME: The stream reader stops, instead of doing a continous read.
                    Console.WriteLine("End of file");
                }
            });

            Task.WaitAll(t1, t2);
        }
    }
}

编辑:作为一个实际示例,这对于第三方进程正在生成需要读取和处理的日志条目的情况非常有用。您可以将其视为日志文件阅读器,如果这样可以使应用程序更清晰。

2 个答案:

答案 0 :(得分:1)

通过检查line == null属性,您可以在streamReader.EndOfFile执行等待。使用Thread.Sleep(1000)不是一个理想的解决方案,有点hacky,我想还有其他更好的替代解决方案。 : - )

using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read | FileShare.Write))
using (var streamReader = new StreamReader(fileStream))
{
    string line;
    bool running = true; // we may want to terminate this loop in some condition.
    while (running)
    {
        line = streamReader.ReadLine();
        if (line != null)
        {
            Console.WriteLine(line);
        }
        else // as per edit, the whole else block can be omitted.
        {
            while (streamReader.EndOfStream)
            {
                Thread.Sleep(1000); // wait around for n time. This could end up in an infinte loop if the file is not written to anymore. 
            }
        }
   }

   // FIXME: The stream reader stops, instead of doing a continous read.
   Console.WriteLine("End of file");
}

编辑:您可以不使用else块:

else 
{ 
    while (streamReader.EndOfStream) 
    { 
        Thread.Sleep(1000) 
    } 
}

像这样:

while (running)
{
    line = streamReader.ReadLine();
    if (line != null)
    {
        Console.WriteLine(line);
    }
}

答案 1 :(得分:0)

您需要同步机制,在这种情况下我使用AutoResetEvent。 根据您的代码所需的更改是。

const string file = "test.log";
// Adds new line.
AutoResetEvent signal = new AutoResetEvent(false);

streamWriter.Flush();
// Adds new line.
signal.Set();

File.Delete(file);
// Adds new line
signal.Set();

Thread.Sleep(500);
// Replaces with.
signal.WaitOne();

while ((line = streamReader.ReadLine()) != null) Console.WriteLine(line);
// Replaces with.
while ((line = streamReader.ReadLine()) != null)
{
    signal.WaitOne();
    Console.WriteLine(line);
}

完整代码

const string file = "test.log";
AutoResetEvent signal = new AutoResetEvent(false);

// Writes lines to the log file.
var t1 = Task.Factory.StartNew(() =>
{
    using (var fileStream = File.Open(file, FileMode.Create, FileAccess.Write, FileShare.Read))
    {
        using (var streamWriter = new StreamWriter(fileStream))
        {
            for (var i = 0; i < 10; i++)
            {
                streamWriter.WriteLine(i);
                streamWriter.Flush();
                signal.Set();
                Thread.Sleep(10);
            }
        }
    }

    File.Delete(file);
    signal.Set();
});

// Reads lines from the log file.
var t2 = Task.Factory.StartNew(() =>
{
    signal.WaitOne();
    using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Delete | FileShare.Read | FileShare.Write))
    {
        using (var streamReader = new StreamReader(fileStream))
        {
            string line;
            while ((line = streamReader.ReadLine()) != null)
            {
                signal.WaitOne();
                Console.WriteLine(line);
            }

            // FIXME: The stream reader stops, instead of doing a continous read.
            Console.WriteLine("End of file");
        }
    }
});

Task.WaitAll(t1, t2);